简体   繁体   中英

SQL query to compare multiple columns in same table in oracle

I have a requirement to find emplid having data difference in same table. Table consist of 50-60 columns.. I need to check if any column has change in data from previous row, emplidshould get pick up as well as if any new employee get add that also needs to pick up..

I have created a basic query and it is working but need some way to achieve same purpose as I do not want to write every column name.

My query:

select 
    emplid 
from 
    ps_custom_tbl t, ps_custom_tbl prev_t 
where 
    prev_t.emplid = t.emplid 
    and t.effdt = (select max effdt from ps_custom_tbl t2 
                   where t2.emplid = t.emplid) 
    and prev_t.effdt = (select max(effdt) from ps_custom_tbl prev_t2 
                        where emplid = prev_t.emplid and effdt < t.effdt)
    and (t.first_name prev_t.first_name Or t.last_name prev_t.last_name …. 50 columns);

Can you please suggest another way to achieve same thing?

You will have to write all columns in some sense no matter what you do.

In terms of comparing current and previous, you might find this easier

select 
  col1,
  col2,
  ...
  lag(col1) over ( partition by empid order by effdt ) as prev_col1,
  lag(col2) over ( partition by empid order by effdt ) as prev_col2
  ...

and then you comparison will be along the lines of

select *
from (  <query above >
where
  decode(col1,prev_col1,0,1) = 1 or 
  decode(col2,prev_col2,0,1) = 1 or 
  ...

The use of DECODE in this way handles the issues of nulls.

You can use MINUS.

if no_data then both are the same, if there are some records - mean that there is a difference between

create table emp as select * from hr.employees;

insert into emp select employee_id+1000, first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id, 
 decode(department_id ,30,70, department_id)
 from hr.employees;

select first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id, department_id  
from emp where employee_id <= 1000
minus
select first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id, department_id  
from emp where employee_id > 1000;

But you have to list all columns, because if you have eg different dates or ids - they will be compared too. But it's easier to list columns in SELECT clause then write for everyone WHERE condition. Maybe it will help.

-- or if different tables and want to compare all cols simply do

drop table emp;
create table emp as select * from hr.employees;
    
create table emp2  as 
select employee_id, first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id, 
 decode(department_id ,30,70, department_id) department_id
 from hr.employees;

select * from emp
minus 
select * from emp2;

---- ADD DATE CRITERIA

-- yes, you can add date criteria and using analytical functions check which -- is newer and which is

older and then compare one to another. like below:

drop table emp;
create table emp as select * from hr.employees;
        
insert into emp 
        select
           employee_id, 
           first_name, 
           last_name, 
           email, 
           phone_number, 
           hire_date+1, 
           job_id, 
           salary, 
           commission_pct, 
           manager_id, 
           decode(department_id ,30,70, department_id)
        from hr.employees;
        
     with data as   --- thanks to WITH you retrieve data only once
            (select employee_id, first_name, last_name, email, phone_number, 
                hire_date, 
                row_number() over(partition by employee_id order by hire_date desc) rn  -- distinguish newer and older record, 
               job_id, salary, commission_pct, manager_id, department_id  
        from emp)
        select employee_id, first_name, last_name, email, phone_number, department_id from data where rn  = 1
        MIUNUS--- find the differences
        select employee_id, first_name, last_name, email, phone_number, department_id from data where rn = 2;

My requirement is to send out data to managers, they change any/all/none of the data in the columns, and send back to me. I then have to identify each column that has a difference from what I sent, and mark those columns as changed for a central office reviewer to visually scan and approve/deny the changes for integration back into the central data set.

This solution may not fit your needs of course, but a template structure is offered here that you can augment to meet your needs no matter the number of columns. In the case of your question, 50-60 columns will make this SQL query huge, but I've written heinously long queries in the past with great success. Add columns a few at a time rather than all wholesale according to this template and see if they work along the way.

You could easily write pl/sql to write this query for you for the tables in question. This would get very cumbersome if you had to compare columns from 3 or more tables or bi-directional changes. I only care about single direction changes. Did the person change my original row columns or not. If so, what columns did they change, and what was my before value and what is their after value, and show me nothing else please.

In other words, only show me rows with columns that have changes with their before values and nothing else.

create table thing1 (id number, firstname varchar2(10), lastname varchar2(10));
create table thing2 (id number, firstname varchar2(10), lastname varchar2(10));

insert into thing1 values (1,'Buddy', 'Slacker');
insert into thing2 values (1,'Buddy', 'Slacker');
insert into thing1 values (2,'Mary', 'Slacker');
insert into thing2 values (2,'Mary', 'Slacke');
insert into thing1 values (3,'Timmy', 'Slacker');
insert into thing2 values (3,'Timm', 'Slacker');
insert into thing1 values (4,'Missy', 'Slacker');
insert into thing2 values (4,'Missy', 'Slacker');
commit;

Un-comment commented select * queries one at a time after each data set to understand what is in each data set at each stage of the refinement process.

with rowdifferences as
(
select
id
,firstname
,lastname
from thing2
minus
select
id
,firstname
,lastname
from thing1
)
--select * from rowdifferences
,thing1matches as
(
select
t1.id
,t1.firstname
,t1.lastname
from thing1 t1
join rowdifferences rd on t1.id = rd.id
)
--select * from thing1matches
, col1differences as
(
select
id
,firstname
from rowdifferences
minus
select
id
,firstname
from thing1matches
)
--select * from col1differences
, col2differences as
(
select
id
,lastname
from rowdifferences
minus
select
id
,lastname
from thing1matches
)
--select * from col2differences
,truedifferences as
(
select
case when c1.id is not null then c1.id
when c2.id is not null then c2.id
end id
,c1.firstname
,c2.lastname
from col1differences c1
full join col2differences c2 on c1.id = c2.id
)
--select * from truedifferences
select
t1m.id
,case when td.firstname is not null then t1m.firstname end beforefirstname
,td.firstname afterfirstname
,case when td.lastname is not null then t1m.lastname end beforelastname
,td.lastname afterlastname
from thing1matches t1m
join truedifferences td on t1m.id = td.id
;

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM