I have two similar tables (Table_A and Table_B) and They share the same Unique identifier (Column Employee_Number). And they also have similar Data.
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;
However, one of Table B's records got changed:
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;
This is what I did to find out what column was changed:
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');
Result Of the Query
Employee_Number NAME ADDRESS TEL_NO SOCIAL_SEC_NO
--------------- --------- --------- --------- --------------
1 NO_CHANGE CHANGED NO_CHANGE CHANGED
3 NO_CHANGE NO_CHANGE NO_CHANGE CHANGED
I was wondering if there's a better and more efficient way to find out which record and column was changed without the use of triggers or any other DDL and DML?
Database Details:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
Thanks!
Variation on a theme... Problem solved many years ago on AskTom, credit to Marco Stefanelli who came up with the core idea.
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.
This query will identify also the rows in one table that don't have a correspondent (row with the same employee_number
) in the other table. The way it works is, the count(*)
is 2 if the exact same row exists in both table.
If you want to exclude the rows that are in one table and not in the other (at all - meaning no matching employee_number
), add another "column" to the middle select
, for count(*) over (partition by employee_number)
- this will be 1 if an employee_number
appears in one table but not in the other, so in the where
clause in the outer query ask that this number be = 2
.
A simpler version (a bit harder to "hack" to exclude the rows that don't have a matching employee_number
in the other table though):
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
;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.