找出两个表之间更新的记录和列

时间:2016-12-19 00:19:42

标签: sql oracle

我有两个相似的表(Table_A和Table_B),它们共享相同的唯一标识符(Column Employee_Number列)。 他们也有类似的数据。

Create table Table_A
(
    Employee_Number varchar2(100),
    name            varchar2(100),
    address         varchar2(100),
    tel_no          varchar2(100),
    social_sec_no   varchar2(100)
);

INSERT INTO Table_A  (Employee_Number, name, address, tel_no, social_sec_no) values ('1', 'emp 1', 'home1', '1111', '11111');
INSERT INTO Table_A  (Employee_Number, name, address, tel_no, social_sec_no) values ('2', 'emp 2', 'home2', '2222', '22222');
INSERT INTO Table_A  (Employee_Number, name, address, tel_no, social_sec_no) values ('3', 'emp 3', 'home3', '3333', '33333');
INSERT INTO Table_A  (Employee_Number, name, address, tel_no, social_sec_no) values ('4', 'emp 4', 'home4', '4444', '44444');
INSERT INTO Table_A  (Employee_Number, name, address, tel_no, social_sec_no) values ('5', 'emp 5', 'home5', '5555', '55555');

commit;

create table Table_b
as
select  *
from    Table_A;

然而,表B中的一条记录发生了变化:

update  Table_b
set     social_sec_no = '99999'
where   Employee_Number = '1';

update  Table_b
set     social_sec_no = 'xxxx'
where   Employee_Number = '3';

update  Table_b
set     address = 'office'
where   Employee_Number = '1';

commit;

我这样做是为了找出哪些列被更改了:

SELECT  *
FROM    (select AX.Employee_Number
              , CASE WHEN AX.name          <> bx.NAME          THEN 'CHANGED' else 'NO_CHANGE' END  name
              , CASE WHEN AX.address       <> bx.address       THEN 'CHANGED' else 'NO_CHANGE' END  address
              , CASE WHEN AX.tel_no        <> bx.tel_no        THEN 'CHANGED' else 'NO_CHANGE' END  tel_no
              , CASE WHEN AX.social_sec_no <> bx.social_sec_no THEN 'CHANGED' else 'NO_CHANGE' END  social_sec_no
        from    Table_A ax
              , Table_B bx
        where   ax.Employee_Number = bx.Employee_Number
        and    (ax.name          <> bx.name
        or      ax.address       <> bx.address
        or      ax.tel_no        <> bx.tel_no
        or      ax.social_sec_no <> bx.social_sec_no))
WHERE   1=1
AND    (name          = 'CHANGED'
OR      address       = 'CHANGED'
OR      tel_no        = 'CHANGED'
OR      social_sec_no = 'CHANGED');   

查询结果

Employee_Number NAME        ADDRESS     TEL_NO      SOCIAL_SEC_NO     
--------------- ---------   ---------   ---------   --------------
1               NO_CHANGE   CHANGED     NO_CHANGE   CHANGED
3               NO_CHANGE   NO_CHANGE   NO_CHANGE   CHANGED       

我想知道是否有更好更有效的方法来找出哪个记录​​和列已更改 不使用触发器或任何其他DDL和DML?

数据库详细信息:

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production

谢谢!

1 个答案:

答案 0 :(得分:2)

主题的变化......许多年前在AskTom上解决了问题,感谢Marco Stefanelli提出核心理念。

select * 
from (
  select source, employee_number, name, address, tel_no, social_sec_no,
         count(*) over ( partition by employee_number, 
                       name, address, tel_no, social_sec_no) as cnt
  from (
         select 'table_a' as source, 
                employee_number, name, address, tel_no, social_sec_no
           from table_a
         union all
         select 'table_b' as source, 
                employee_number, name, address, tel_no, social_sec_no
           from table_b
       )
)
where cnt = 1
order by employee_number, source
;

SOURCE   EMPLOYEE_NUMBER NAME       ADDRESS      TEL_NO     SOCIAL_SEC_NO    CNT
-------  --------------- ---------- ------------ ---------- ------------- ------
table_a  1               emp 1      home1        1111       11111              1
table_b  1               emp 1      office       1111       99999              1
table_a  3               emp 3      home3        3333       33333              1
table_b  3               emp 3      home3        3333       xxxx               1

4 rows selected.

此查询还将识别一个表中不具有另一个表中的通信者(具有相同employee_number的行)的行。它的工作方式是,如果两个表中都存在完全相同的行,则count(*)为2。

如果要排除一个表中的行而不是另一个表中的行(完全没有匹配employee_number),请添加另一个&#34;列&#34;中间selectcount(*) over (partition by employee_number) - 如果employee_number出现在一个表中而不出现在另一个表中,则该值为1,因此在外部查询的where子句中要求此号码为= 2

一个更简单的版本(更难以#34; hack&#34;排除那些在另一个表中没有匹配employee_number的行):

select max(source) as source, 
       employee_number, name, address, tel_no, social_sec_no
from   (
         select 'table_a' as source, 
                employee_number, name, address, tel_no, social_sec_no
           from table_a
         union all
         select 'table_b' as source, 
                 employee_number, name, address, tel_no, social_sec_no
           from table_b
       )
group by employee_number, name, address, tel_no, social_sec_no
having count(*) = 1
order by employee_number, source
;