我编写了一个查询来比较oracle中2个数据库表的列/数据,它将打印基于关键列(分区子句中的字段)的2个表行中存在差异的记录,如果没有差异,则对于任何特定字段,它将打印 null,对于存在差异的字段,它将打印那里的值(其中 row_cnt = 2),如果任何表中有一些额外的记录,那么它也会打印它(其中 row_cnt =1) .
Sample script --
create table src_tab (id number,name varchar2(10), job varchar2(10), sal number);
create table tgt_tab (id number,name varchar2(10), job varchar2(10), sal number);
insert into src_tab values (1, 'john', 'not sure', 100);
insert into src_tab values (2, 'kevin', 'unemp', 200);
insert into src_tab values (3, 'chad', 'emplyd', 400);
insert into src_tab values (4, 'page', 'NA', 500);
insert into src_tab values (5, 'emmy', 'desk', 600);
insert into tgt_tab values (1, 'john', 'not sure', 150);
insert into tgt_tab values (2, 'kevin', 'unemp', 200);
insert into tgt_tab values (3, 'chad', 'empl', 400);
insert into tgt_tab values (4, 'page', 'NA', 500);
Comparison query --
Select * from (select id,name,
case NEW_CNT when 1 then 'TGT_TAB' else 'SRC_TAB' end tbl,
ROW_CNT,
case when count(job) over(partition by id,name)
between 1 and count(distinct job) over(partition by id,name)
then job end job,
case when count(sal) over(partition by id,name)
between 1 and count(distinct sal) over(partition by id,name)
then sal end sal
FROM (
select
id,name, job, sal,
sum(NEW_CNT) NEW_CNT, count(*) over(partition by id,name) ROW_CNT
FROM (
select
id,name, job, sal,
-1 NEW_CNT
from src_tab O
union all
select
id,name, job, sal,
1 NEW_CNT
from tgt_tab N
)
group by
id,name, job, sal
having sum(NEW_CNT) != 0
)
order by 1, 2, new_cnt)
;
由于这个查询是为oracle 19c数据库编写的,现在我需要在Sybase数据库上运行这个查询,但是当我在Sybase上尝试它时,我发现那里不允许COUNT(DISTINCT)窗口函数,所以现在寻找替代方法要打印与 Sybase 上相同的输出,我尝试使用领先/滞后函数,因为 Sybase 支持它,但输出不好。
Alternative Tried --
case when sal <>
lead(sal,1,sal) over (partition by id,name order by id) then sal end sal_lead
Expected Result --
ID NAME TBL ROW_CNT JOB SAL
1 john SRC_TAB 2 null 100
1 john TGT_TAB 2 null 150
3 chad SRC_TAB 2 emplyd null
3 chad TGT_TAB 2 empl null
5 emmy SRC_TAB 1 desk 600
我们同时使用Sybase DB ASE和IQ,如果查询可以与Sybase-IQ兼容那么也很好。 如果我需要更多信息来理解我在 orcale 中编写的查询,请告诉我。
编辑: 我发现一种适用于 Sybase-IQ 的替代方案如下,如果有人有更好的方法,请发表评论!
with t1 as (
select 'SRC_TAB' tbl,
s.*
from src_tab s
union all
select 'TGT_TAB' tbl,
t.*
from tgt_tab t
),
t2 as (
select t1.*,
case max(job) over(partition by id,name)
when min(job) over(partition by id,name) then 1
else 0
end same_job_flag,
case max(sal) over(partition by id,name)
when min(sal) over(partition by id,name) then 1
else 0
end same_sal_flag,
count(*) over(partition by id,name) row_cnt
from t1
)
select id,
name,
tbl,
row_cnt,
case
when row_cnt = 1 then job
when same_job_flag = 0 then job
end job,
case
when row_cnt = 1 then sal
when same_sal_flag = 0 then sal
end sal
from t2
where same_job_flag = 0
or same_sal_flag = 0
or row_cnt = 1
order by id,
name,
tbl;
背景:
如果 OP 的 IQ 查询(带有 CTE 和窗口函数)生成预期结果,那么 OP 可能需要考虑一个选项:
我能够将在 ASE 中运行的几个查询拼凑在一起,生成预期的结果......
向数据集中添加更多行:
insert into tgt_tab values ( 6, 'bob', 'unemp', 350) -- TGT_TAB only row
insert into src_tab values (10, 'sara', 'desk', 450) -- common key but with differences
insert into tgt_tab values (10, 'sara', 'umenp', 300) -- in both non-key columns
一个4路联合查询:
-- only in src_tab
select s.id,s.name,tbl='SRC_TAB',ROW_CNT=1,s.job,s.sal
from src_tab s
where not exists(select 1
from tgt_tab t
where t.id = s.id
and t.name = s.name)
union all
-- only in tgt_tab
select t.id,t.name,tbl='TGT_TAB',ROW_CNT=1,t.job,t.sal
from tgt_tab t
where not exists(select 1
from src_tab s
where t.id = s.id
and t.name = s.name)
union all
-- common key but job and/or sal are different (src_tab)
select s.id,s.name,tbl='SRC_TAB',ROW_CNT=2,
case when s.job != t.job then s.job else NULL end as job,
case when s.sal != t.sal then s.sal else NULL end as sal
from src_tab s
join tgt_tab t
on s.id = t.id
and s.name = t.name
where s.job != t.job
or s.sal != t.sal
union all
-- common key but job and/or sal are different (tgt_tab)
select s.id,s.name,tbl='TGT_TAB',ROW_CNT=2,
case when s.job != t.job then t.job else NULL end as job,
case when s.sal != t.sal then t.sal else NULL end as sal
from src_tab s
join tgt_tab t
on s.id = t.id
and s.name = t.name
where s.job != t.job
or s.sal != t.sal
order by id,name,tbl
我们可以通过使用 2 行虚拟/派生表将公共键匹配拆分为 2 个单独的行,将其缩减为 3 路并集:
-- only in src_tab
select s.id,s.name,tbl='SRC_TAB',ROW_CNT=1,s.job,s.sal
from src_tab s
where not exists(select 1
from tgt_tab t
where t.id = s.id
and t.name = s.name)
union all
-- only in tgt_tab
select t.id,t.name,tbl='TGT_TAB',ROW_CNT=1,t.job,t.sal
from tgt_tab t
where not exists(select 1
from src_tab s
where t.id = s.id
and t.name = s.name)
union all
-- common key but job and/or sal are different
select s.id,s.name,dt.tbl,ROW_CNT=2,
case when dt.tbl = 'SRC_TAB' and s.job != t.job then s.job
when dt.tbl = 'TGT_TAB' and s.job != t.job then t.job
else NULL
end as job,
case when dt.tbl = 'SRC_TAB' and s.sal != t.sal then s.sal
when dt.tbl = 'TGT_TAB' and s.sal != t.sal then t.sal
else NULL
end as sal
from src_tab s
join tgt_tab t
on s.id = t.id
and s.name = t.name
join (select 'SRC_TAB'
union all
select 'TGT_TAB'
) dt (tbl)
on 1=1
where s.job != t.job
or s.sal != t.sal
order by id,name,tbl
注意事项:
ROW_CNT
值只是硬编码值(这些查询生成 ROW_CNT
的唯一原因是匹配 OP 的预期输出)这两个查询都会生成以下内容:
id name tbl ROW_CNT job sal
----------- ---------- ------- ----------- ---------- -----------
1 john SRC_TAB 2 NULL 100
1 john TGT_TAB 2 NULL 150
3 chad SRC_TAB 2 emplyd NULL
3 chad TGT_TAB 2 empl NULL
5 emmy SRC_TAB 1 desk 600
6 bob TGT_TAB 1 unemp 350
10 sara SRC_TAB 2 desk 450
10 sara TGT_TAB 2 umenp 300
注意事项:
ASE 16.0 SP04 PL04
实例生成的结果我猜OP的问题是一个更大项目的最小的、可重现的示例,需要针对一组表运行
diff
。
如果是这种情况,那么很快就会发现到目前为止提出的查询(OP 的查询,我的查询)都没有很好地“扩展”。所有这些查询都针对特定的表对、特定的列集和特定的数据类型集进行硬编码。
对于一个完整的
diff
流程OP需要考虑:
case
语句)= NULL
和 is NULL
的行为不同)构建一个强大的工具需要大量的时间和精力,特别是如果尝试用 SQL 构建整个工具。需要考虑的一些想法:
awk
、perl
、ruby
、c(++)
、java
)rs_subcmp
(操作系统级别,基于 Java 的实用程序)和 Data Assurance
(操作系统级别,基于 Java,许可证$$ $ed 实用程序))