标识值与先前版本相比已更改的列

时间:2018-06-25 09:09:44

标签: oracle oracle11g

我有一张表,其中某些列的值可以更改。我的要求是确定值已更改的items(id)。例如

输入:

ID  VALUE1  VALUE2  VALUE3
1    A       B       C
1    X       B       C
2    D       E       F
2    D       E       F
3    G       H       I
3    S       H       T

所需的输出:

ID  VALUE1  VALUE2  VALUE3
1   X       
3   S               T

我正在使用Oracle SQL。我们将不胜感激

2 个答案:

答案 0 :(得分:1)

这可能是一种方法,假设您需要按某列对记录进行排序(我添加了row_num列只是为了说明):

select *
from (
    select ID, 
           case when lag(value1) over (partition by ID order by row_num) != value1 then value1 end as value1,
           case when lag(value2) over (partition by ID order by row_num) != value2 then value2 end as value2,
           case when lag(value3) over (partition by ID order by row_num) != value3 then value3 end as value3
    from yourTable
    )
where value1 is not null
   or value2 is not null
   or value3 is not null

这使用lag来获取同一row_num的前一行中的值(按ID排序),然后简单地检查是否存在至少一个差异。

使用您的样本数据,

with yourTable(row_num, ID, VALUE1, VALUE2, VALUE3) as (
    select 1, 1, 'A', 'B', 'C' from dual union all
    select 2, 1, 'X', 'B', 'C' from dual union all
    select 3, 2, 'D', 'E', 'F' from dual union all
    select 4, 2, 'D', 'E', 'F' from dual union all
    select 5, 3, 'G', 'H', 'I' from dual union all
    select 6, 3, 'S', 'H', 'T' from dual 
)
select *
from (
    select ID, 
           case when lag(value1) over (partition by ID order by row_num) != value1 then value1 end as value1,
           case when lag(value2) over (partition by ID order by row_num) != value2 then value2 end as value2,
           case when lag(value3) over (partition by ID order by row_num) != value3 then value3 end as value3
    from yourTable
    )
where value1 is not null
   or value2 is not null
   or value3 is not null  

给予

        ID VALUE1 VALUE2 VALUE3
---------- ------ ------ ------
         1 X                   
         3 S             T     

2 rows selected.

答案 1 :(得分:0)

您的解决方案需要三个部分。

  1. 您需要定义更改的顺序。在您的示例中,它是“从上到下”。在表格中,您可能会有日期列或数字ID。

  2. 如@Aleksej所建议的,然后您可以使用LAG(value) OVER (... ORDER BY ...)

  3. 访问先前的值
  4. 您需要一个比较函数,该函数可以正确处理NULL值。这有点痛苦,并且有更多解决方案,但没有一个是好的。我建议使用DECODE(old_value, new_value, 0, 1)=1,有关其他示例,请参见here

我已向您的表中添加了一些额外的行,以测试涉及NULL值的更改:

CREATE TABLE mytable (id NUMBER, value1 VARCHAR2(1), value2 VARCHAR2(1), value3 VARCHAR2(1), t TIMESTAMP DEFAULT SYSTIMESTAMP);
INSERT INTO mytable (id,value1,value2,value3) VALUES (1, 'A','B','C');
INSERT INTO mytable (id,value1,value2,value3) VALUES (1, 'X','B','C');
INSERT INTO mytable (id,value1,value2,value3) VALUES (2, 'D','E','F');
INSERT INTO mytable (id,value1,value2,value3) VALUES (2, 'D','E','F');
INSERT INTO mytable (id,value1,value2,value3) VALUES (3, 'G','H','I');
INSERT INTO mytable (id,value1,value2,value3) VALUES (3, 'S','H','T');
INSERT INTO mytable (id,value1,value2,value3) VALUES (3, 'S','H',NULL);
INSERT INTO mytable (id,value1,value2,value3) VALUES (3, 'S','H',NULL);
INSERT INTO mytable (id,value1,value2,value3) VALUES (3, 'S','U','T');


SELECT ID,
       CASE WHEN value1_changed=1 THEN value1 END AS value1, 
       CASE WHEN value2_changed=1 THEN value2 END AS value2,
       CASE WHEN value3_changed=1 THEN value3 END AS value3,
       value1_changed,
       value2_changed,
       value3_changed
  FROM (
        SELECT id, value1, value2, value3,
               DECODE(value1, LAG(value1) OVER (PARTITION BY ID ORDER BY t), 0, 1) value1_changed,       
               DECODE(value2, LAG(value2) OVER (PARTITION BY ID ORDER BY t), 0, 1) value2_changed,
               DECODE(value3, LAG(value3) OVER (PARTITION BY ID ORDER BY t), 0, 1) value3_changed,
               row_number() OVER (PARTITION BY ID ORDER BY t) AS r, t
          FROM mytable
       ) 
 WHERE r > 1
   AND value1_changed + value2_changed + value3_changed >= 0;


ID  value1 value2 value3  changed1 changed2 changed3
 1  X                     1        0        0
 3  S             T       1        0        1
 3                        0        0        1
 3                U       0        0        1

当value3从'T'变为NULL时,请不要在第三行。它会正确报告,但只有新值NULL。