如何获取行的最后更新

时间:2019-04-30 18:40:49

标签: sql oracle window-functions

目标: 对于每个“ 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
)

但是我想做同样的事情而无需第二次调用表。

谢谢!

3 个答案:

答案 0 :(得分:1)

如果您不需要返回IDCONT不变的STATEID(这将是一个C),那会更简单。 REFTABLE一趟可能看起来像这样;看看它是否对 reality 有帮助。

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>

答案 1 :(得分:1)

这是一种方法:

cb

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

答案 2 :(得分:0)

使用“ FIRST_VALUE”确实可以进行更改,但是您可以依靠表中的单个更改吗?其他更改不会使此操作无效并导致错误的数据吗?

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

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

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中做到了这一点,但它必须与之接近。