【问题标题】:How to get rows with last update on column如何获取列上最后更新的行
【发布时间】:2019-09-19 11:18:49
【问题描述】:

目标: 对于每个“IDCONT”,我需要获取“DAY_ID”,其中我对“STATE_ID”进行了最后一次更改/更新。

例子:

with reftable as (
 select 1 as PROCESSID, 'A' as IDCONT, 'X' as STATEID, '10' AS DAY_ID union all
 select 2 as PROCESSID, 'A' as IDCONT, 'X' as STATEID, '11' AS DAY_ID union all
 select 3 as PROCESSID, 'A' as IDCONT, 'Y' as STATEID, '12' AS DAY_ID union all
 select 4 as PROCESSID, 'A' as IDCONT, 'Y' as STATEID, '13' AS DAY_ID union all

 select 1 as PROCESSID, 'B' as IDCONT, 'N' as STATEID, '14' AS DAY_ID union all
 select 2 as PROCESSID, 'B' as IDCONT, 'N' as STATEID, '15' AS DAY_ID union all
 select 3 as PROCESSID, 'B' as IDCONT, 'M' as STATEID, '16' AS DAY_ID union all

 select 1 as PROCESSID, 'C' as IDCONT, 'X' as STATEID, '11' AS DAY_ID union all
 select 2 as PROCESSID, 'C' as IDCONT, 'X' as STATEID, '18' AS DAY_ID union all
) ...

预期结果:

PROCESSID   IDCONT   STATID   DAYID
3           A        Y        12
2           B        N        15
1           C        X        11        

我解决了这个问题:

...
SELECT IDCONT, STATEID, MIN(DAY_ID)
FROM REFTABLE 
WHERE (IDCONT, STATEID) IN (
   SELECT IDCONT, FIRST_VALUE(STATEID) OVER PARTITION BY IDCONT ORDER BY PROCESSID DESC) AS STATEID
   FROM REFTABLE
)

但我想做同样的事情,而不需要第二次调用表。

谢谢!

【问题讨论】:

  • 看看这个问题,它有点不同,但我认为它是相同的模式。 stackoverflow.com/questions/121387/…
  • 您的 SQL 不起作用。除其他问题外,您的 REFTABLE 定义中没有 FROM DUAL,它会在 FIRST_VALUE(STATEID) 处引发 INVALID RELATIONAL OPERATOR 错误。
  • 此外,我认为 PROCESSID=2 和 IDCONT = 'B' 的 STATEID = 'M'(不是 'N')的值
  • 我不明白你的结果。比如为什么是15?如果数据更改 XXYYXX 怎么办?你选哪个“X”?

标签: sql oracle window-functions


【解决方案1】:

这是一种方法:

select r.*
from (select r.*,
             lag(stateid) over (partition by idcont order by day_id) as prev_stateid,
             first_value(stateid) over (partition by idcont order by day_id desc) as last_stateid
      from reftable r
     ) r
where stateid = last_stateid and (prev_stateid is null or prev_stateid <> stateid);

但是,这不处理状态变回先前状态的情况。如有必要,可以添加该逻辑。

【讨论】:

    【解决方案2】:

    如果您不需要返回 IDCONT 其 STATEID 没有改变(那将是一个 C),它会更简单。一次REFTABLE 行程可能是这样的;看看它在现实中是否有任何好处。

    SQL> with reftable as (
      2   select 1 as PROCESSID, 'A' as IDCONT, 'X' as STATEID, '10' AS DAY_ID from dual union all
      3   select 2 as PROCESSID, 'A' as IDCONT, 'X' as STATEID, '11' AS DAY_ID from dual union all
      4   select 3 as PROCESSID, 'A' as IDCONT, 'Y' as STATEID, '12' AS DAY_ID from dual union all
      5   select 4 as PROCESSID, 'A' as IDCONT, 'Y' as STATEID, '13' AS DAY_ID from dual union all
      6   --
      7   select 1 as PROCESSID, 'B' as IDCONT, 'N' as STATEID, '14' AS DAY_ID from dual union all
      8   select 2 as PROCESSID, 'B' as IDCONT, 'N' as STATEID, '15' AS DAY_ID from dual union all
      9   select 3 as PROCESSID, 'B' as IDCONT, 'M' as STATEID, '16' AS DAY_ID from dual union all
     10   --
     11   select 1 as PROCESSID, 'C' as IDCONT, 'X' as STATEID, '11' AS DAY_ID from dual union all
     12   select 2 as PROCESSID, 'C' as IDCONT, 'X' as STATEID, '18' AS DAY_ID from dual
     13  ),
     14  inter as
     15    (select processid, idcont, stateid, day_id,
     16            case when nvl(lag(stateid) over
     17                            (partition by idcont order by processid  ), '?') <> stateid then
     18                      row_number() over (partition by idcont order by processid )
     19            end grp
     20     from reftable
     21    )
     22  select processid, idcont, stateid, day_id
     23  from inter i
     24  where grp = (select max(i1.grp)
     25              from inter i1
     26              where i1.idcont = i.idcont)
     27  order by idcont, processid;
    
     PROCESSID IDCONT     STATEID    DAY_ID
    ---------- ---------- ---------- ----------
             3 A          Y          12
             3 B          M          16
             1 C          X          11
    
    SQL>
    

    【讨论】:

      【解决方案3】:

      使用“FIRST_VALUE”确实会给您带来变化,但您能相信表中只有一个变化吗?其他更改不会使这一点失效并导致错误数据吗?

      我改用了 LAG 函数,但它没有返回 IDCONT C,因为它没有发生变化。

      使用 CTE 获取数据,然后使用查询进行过滤可能会更快(因为您不能将 LAG 或 FIRST_VALUE 放在 where 子句中)。它会阻止再次访问数据库。

      CREATE TABLE REFTABLE
          ([PROCESSID] int, [IDCONT] varchar(1), [STATEID] varchar(1), [DAY_ID] int)
      ;
      
      INSERT INTO REFTABLE
          ([PROCESSID], [IDCONT], [STATEID], [DAY_ID])
      VALUES
          (1, 'A', 'X', 10),
          (2, 'A', 'X', 11),
          (3, 'A', 'Y', 12),
          (4, 'A', 'Y', 13),
          (1, 'B', 'N', 14),
          (2, 'B', 'N', 15),
          (3, 'B', 'M', 16),
          (1, 'C', 'X', 11),
          (2, 'C', 'X', 18)
      ;
      
      with chgfound as (SELECT TOP 100 PERCENT PROCESSID, IDCONT, STATEID, DAY_ID, LAG(STATEID) OVER(PARTITION BY IDCONT ORDER BY IDCONT, PROCESSID) as LastState
      from REFTABLE
      order by IDCONT, PROCESSID
      )
      select * from chgfound where STATEID !=LastState
      

      http://www.sqlfiddle.com/#!18/086134

      还刚刚注意到您有 Oracle 标记。我在 SQL Server 中执行此操作,但它必须非常接近。

      【讨论】:

        猜你喜欢
        • 2014-05-09
        • 2010-11-26
        • 2015-01-03
        • 1970-01-01
        • 2013-12-09
        • 1970-01-01
        • 2020-07-10
        • 2018-05-20
        • 1970-01-01
        相关资源
        最近更新 更多