2017-01-09 63 views
1

我有两个表(一个旧的档案和一个新的档案)与3列,其中第一个列是一种层次结构,其他两个是无法重复的值。每个层次结构需要检查两个表的序列是否完全相同。如果不是,我希望它显示整个层次结构的变化。因此,例如:如何找到两个表之间的序列差异?

表1:

colum1  colum2 colum3 
-------------------------------- 
| folder1 | detail1 | detail10 | 
-------------------------------- 
| folder1 | detail2 | detail20 | 
-------------------------------- 
| folder1 | detail3 | detail30 | 
-------------------------------- 
| folder2 | detail1 | detail10 | 
-------------------------------- 
| folder2 | detail2 | detail20 | 
-------------------------------- 
| folder2 | detail3 | detail30 | 
-------------------------------- 
| folder3 | detail1 | detail10 | 
-------------------------------- 
| folder3 | detail2 | detail20 | 
-------------------------------- 
| folder3 | detail3 | detail30 | 
-------------------------------- 
| folder4 | detail1 | detail10 | 
-------------------------------- 
| folder4 | detail2 | detail20 | 
-------------------------------- 
| folder4 | detail3 | detail30 | 
-------------------------------- 

表2:

colum1  colum2 colum3 
-------------------------------- 
| folder1 | detail3 | detail30 | 
-------------------------------- 
| folder1 | detail2 | detail20 | 
-------------------------------- 
| folder1 | detail1 | detail10 | 
-------------------------------- 
| folder2 | detail1 | detail10 | 
-------------------------------- 
| folder2 | detail2 | detail20 | 
-------------------------------- 
| folder2 | detail3 | detail30 | 
-------------------------------- 
| folder3 | detail1 | detail10 | 
-------------------------------- 
| folder3 | detail3 | detail30 | 
-------------------------------- 
| folder3 | detail2 | detail20 | 
-------------------------------- 
| folder4 | detail1 | detail10 | 
-------------------------------- 
| folder4 | detail3 | detail30 | 
-------------------------------- 

所以,如果某些值切换或1个层级之间缺少它会显示整个层次。从上面的例子看,结果看起来像这样:

   table1      table2 

colum1  colum2 colum3  colum1 colum2 colum3 
--------------------------------------------------------------- 
| folder1 | detail1 | detail10 | folder1 | detail3 | detail30 | <--- was switched 
--------------------------------------------------------------- 
| folder1 | detail2 | detail20 | folder1 | detail2 | detail20 | 
--------------------------------------------------------------- 
| folder1 | detail3 | detail30 | folder1 | detail1 | detail10 | <--- was switched 
--------------------------------------------------------------- 
| folder3 | detail1 | detail10 | folder3 | detail1 | detail10 | 
--------------------------------------------------------------- 
| folder3 | detail2 | detail20 | folder3 | detail3 | detail30 | <--- was switched 
--------------------------------------------------------------- 
| folder3 | detail3 | detail30 | folder3 | detail2 | detail20 | <--- was switched 
--------------------------------------------------------------- 
| folder4 | detail1 | detail10 | folder4 | detail1 | detail10 | 
--------------------------------------------------------------- 
| folder4 | detail2 | detail20 |   |   |   | <---missing 
--------------------------------------------------------------- 
| folder4 | detail3 | detail30 | folder4 | detail3 | detail30 | 
--------------------------------------------------------------- 

所以这有点可能吗?我想为此使用SQL,但也许另一种语言更方便?任何提示都表示赞赏。

在此先感谢

+0

怎能算法/查询决定table2中的一行是否已被切换或丢失?例如,为什么结果的最后一行没有标记为缺失,而之前的行标记为已切换? –

+0

有很多方法可以做到这一点。一种方法是使用tablediff工具。 [tablediff utility-msdn](https://msdn.microsoft.com/zh-cn/library/ms162843.aspx) [tablediff utility intro](https://www.simple-talk.com/sql/sql) -tools/sql-server-tablediff-utility /) – SqlZim

+0

@StephanLechner,很好的问题。说实话,如果用户可以看到该行已被切换或丢失,它确实很重要。只要整个层次结构在出现差异时显示 –

回答

1

如果我们增加了一个ID给每个表,并依靠插入顺序意味着什么(或者,如果存在一些其他的序号或排序使用),你可以这样做:

更新检查基于与ROW_NUMBER()的文件夹:

实施例的测试设置:http://rextester.com/VXZIP45848

create table a (
    id int not null identity(1,1) 
    , one varchar(32) 
    , two varchar(32) 
    , three varchar(32)) 
insert into a values 
('folder1','detail1','detail10') 
,('folder1','detail2','detail20') 
,('folder1','detail3','detail30') 
,('folder1','detail 98','detail 198') 
,('folder2','detail1','detail10') 
,('folder2','detail2','detail20') 
,('folder2','detail3','detail30') 
,('folder1','detail99','detail 199') 
,('folder3','detail1','detail10') 
,('folder3','detail2','detail20') 
,('folder3','detail3','detail30') 
,('folder4','detail1','detail10') 
,('folder4','detail2','detail20') 
,('folder4','detail3','detail30'); 


create table b (
    id int not null identity(1,1) 
    , one varchar(32) 
    , two varchar(32) 
    , three varchar(32)) 
insert into b values 
('folder1','detail3','detail30') 
,('folder1','detail2','detail20') 
,('folder1','detail1','detail10') 
,('folder2','detail1','detail10') 
,('folder2','detail2','detail20') 
,('folder2','detail3','detail30') 
,('folder3','detail1','detail10') 
,('folder3','detail3','detail30') 
,('folder3','detail2','detail20') 
,('folder4','detail1','detail10') 
,('folder4','detail3','detail30') 
,('folder5','detail5','detail50'); 

/* show all a and all b when matched and when not */ 

select a.*, b.*, diff=case 
    when a.rn=b.rn then '' 
    when a.id is null then 'not in a' 
    when b.id is null then 'not in b' 
    when a.rn!=b.rn then 'switched' 
    else '?' 
    end 
    from (select * ,rn = row_number() over (partition by one order by id) 
      from a) as a 
     full outer join 
     (select * ,rn = row_number() over (partition by one order by id) 
      from b) as b 
      on a.one=b.one 
      and a.two=b.two 
      and a.three=b.three 

结果:

+------+---------+-----------+------------+------+------+---------+---------+----------+------+----------+ 
| id | one | two | three | rn | id | one | two | three | rn | diff | 
+------+---------+-----------+------------+------+------+---------+---------+----------+------+----------+ 
| 1 | folder1 | detail1 | detail10 | 1 | 3 | folder1 | detail1 | detail10 | 3 | switched | 
| 2 | folder1 | detail2 | detail20 | 2 | 2 | folder1 | detail2 | detail20 | 2 |   | 
| 3 | folder1 | detail3 | detail30 | 3 | 1 | folder1 | detail3 | detail30 | 1 | switched | 
| 4 | folder1 | detail 98 | detail 198 | 4 | NULL | NULL | NULL | NULL  | NULL | not in b | 
| 8 | folder1 | detail99 | detail 199 | 5 | NULL | NULL | NULL | NULL  | NULL | not in b | 
| 5 | folder2 | detail1 | detail10 | 1 | 4 | folder2 | detail1 | detail10 | 1 |   | 
| 6 | folder2 | detail2 | detail20 | 2 | 5 | folder2 | detail2 | detail20 | 2 |   | 
| 7 | folder2 | detail3 | detail30 | 3 | 6 | folder2 | detail3 | detail30 | 3 |   | 
| 9 | folder3 | detail1 | detail10 | 1 | 7 | folder3 | detail1 | detail10 | 1 |   | 
| 10 | folder3 | detail2 | detail20 | 2 | 9 | folder3 | detail2 | detail20 | 3 | switched | 
| 11 | folder3 | detail3 | detail30 | 3 | 8 | folder3 | detail3 | detail30 | 2 | switched | 
| 12 | folder4 | detail1 | detail10 | 1 | 10 | folder4 | detail1 | detail10 | 1 |   | 
| 13 | folder4 | detail2 | detail20 | 2 | NULL | NULL | NULL | NULL  | NULL | not in b | 
| 14 | folder4 | detail3 | detail30 | 3 | 11 | folder4 | detail3 | detail30 | 2 | switched | 
| NULL | NULL | NULL  | NULL  | NULL | 12 | folder5 | detail5 | detail50 | 1 | not in a | 
+------+---------+-----------+------------+------+------+---------+---------+----------+------+----------+ 

由于没有序号或排序,找到的“开关”的情况下是不可能在这种情况下。

实施例的测试设置:http://rextester.com/YEIAN6814

/* since there is no ordinal or sort, 
    finding cases of 'switching' aren't possible in this case. */ 

create table a (one varchar(32), two varchar(32), three varchar(32)) 
insert into a values 
('folder1','detail1','detail10') 
,('folder1','detail2','detail20') 
,('folder1','detail3','detail30') 
,('folder2','detail1','detail10') 
,('folder2','detail2','detail20') 
,('folder2','detail3','detail30') 
,('folder3','detail1','detail10') 
,('folder3','detail2','detail20') 
,('folder3','detail3','detail30') 
,('folder4','detail1','detail10') 
,('folder4','detail2','detail20') 
,('folder4','detail3','detail30'); 

create table b (one varchar(32), two varchar(32), three varchar(32)) 
insert into b values 
('folder1','detail3','detail30') 
,('folder1','detail2','detail20') 
,('folder1','detail1','detail10') 
,('folder2','detail1','detail10') 
,('folder2','detail2','detail20') 
,('folder2','detail3','detail30') 
,('folder3','detail1','detail10') 
,('folder3','detail3','detail30') 
,('folder3','detail2','detail20') 
,('folder4','detail1','detail10') 
,('folder4','detail3','detail30') 
,('folder5','detail5','detail50'); /* added this value for example*/ 

全外连接:显示所有a和所有b要么匹配或不

/* show all a and all b when matched and when not */ 
select a.*, b.* 
    , diff=case 
     when a.one is null then 'not in a' 
     when b.one is null then 'not in b' 
     else '' 
     end 
    from a 
    full outer join b on a.one =b.one 
     and isnull(a.two,'') =isnull(b.two,'') 
     and isnull(a.three,'')=isnull(b.three,'') 
     --and (a.two =b.two or (a.two is null and b.two is null)) 
     --and (a.three=b.three or (a.three is null and b.three is null)) 

+---------+---------+----------+---------+---------+----------+----------+ 
| one | two | three | one | two | three | diff | 
+---------+---------+----------+---------+---------+----------+----------+ 
| folder1 | detail1 | detail10 | folder1 | detail1 | detail10 |   | 
| folder1 | detail2 | detail20 | folder1 | detail2 | detail20 |   | 
| folder1 | detail3 | detail30 | folder1 | detail3 | detail30 |   | 
| folder2 | detail1 | detail10 | folder2 | detail1 | detail10 |   | 
| folder2 | detail2 | detail20 | folder2 | detail2 | detail20 |   | 
| folder2 | detail3 | detail30 | folder2 | detail3 | detail30 |   | 
| folder3 | detail1 | detail10 | folder3 | detail1 | detail10 |   | 
| folder3 | detail2 | detail20 | folder3 | detail2 | detail20 |   | 
| folder3 | detail3 | detail30 | folder3 | detail3 | detail30 |   | 
| folder4 | detail1 | detail10 | folder4 | detail1 | detail10 |   | 
| folder4 | detail2 | detail20 | NULL | NULL | NULL  | not in b | 
| folder4 | detail3 | detail30 | folder4 | detail3 | detail30 |   | 
| NULL | NULL | NULL  | folder5 | detail5 | detail50 | not in a | 
+---------+---------+----------+---------+---------+----------+----------+ 

左联接时:所有ab匹配时

/* show all a, b matches */ 
select a.*, b.* 
from a 
    left join b on a.one =b.one 
     and isnull(a.two,'') =isnull(b.two,'') 
     and isnull(a.three,'')=isnull(b.three,'') 
     --and (a.two =b.two or (a.two is null and b.two is null)) 
     --and (a.three=b.three or (a.three is null and b.three is null)) 

+---------+---------+----------+---------+---------+----------+ 
| one | two | three | one | two | three | 
+---------+---------+----------+---------+---------+----------+ 
| folder1 | detail1 | detail10 | folder1 | detail1 | detail10 | 
| folder1 | detail2 | detail20 | folder1 | detail2 | detail20 | 
| folder1 | detail3 | detail30 | folder1 | detail3 | detail30 | 
| folder2 | detail1 | detail10 | folder2 | detail1 | detail10 | 
| folder2 | detail2 | detail20 | folder2 | detail2 | detail20 | 
| folder2 | detail3 | detail30 | folder2 | detail3 | detail30 | 
| folder3 | detail1 | detail10 | folder3 | detail1 | detail10 | 
| folder3 | detail2 | detail20 | folder3 | detail2 | detail20 | 
| folder3 | detail3 | detail30 | folder3 | detail3 | detail30 | 
| folder4 | detail1 | detail10 | folder4 | detail1 | detail10 | 
| folder4 | detail2 | detail20 | NULL | NULL | NULL  | 
| folder4 | detail3 | detail30 | folder4 | detail3 | detail30 | 
+---------+---------+----------+---------+---------+----------+ 

NOT EXISTS():所有行a不在b

/* all in a that aren't in b */ 
select src='a not in b', * 
    from a 
    where not exists (
     select 1 
     from b 
     where a.one=b.one 
      and isnull(a.two,'') =isnull(b.two,'') 
      and isnull(a.three,'')=isnull(b.three,'') 
    ); 

+------------+---------+---------+----------+ 
| src  | one | two | three | 
+------------+---------+---------+----------+ 
| a not in b | folder4 | detail2 | detail20 | 
+------------+---------+---------+----------+ 

NOT EXISTS():从b不在a所有行

/* all in b that aren't in a */ 
select src='b not in a', * 
    from b 
    where not exists (
     select 1 
     from a 
     where a.one=b.one 
      and isnull(a.two,'') =isnull(b.two,'') 
      and isnull(a.three,'')=isnull(b.three,'') 
    ); 

+------------+---------+---------+----------+ 
| src  | one | two | three | 
+------------+---------+---------+----------+ 
| b not in a | folder5 | detail5 | detail50 | 
+------------+---------+---------+----------+ 

除外:在a不在b

/* distinct values in a that aren't in b */ 
select * from a 
except 
select * from b; 


+---------+---------+----------+ 
| one | two | three | 
+---------+---------+----------+ 
| folder4 | detail2 | detail20 | 
+---------+---------+----------+ 

相交不同的值:不同的值是在a这也是b

/* distinct values that are in a that are also in b */ 
select * from a 
intersect 
select * from b; 


+---------+---------+----------+ 
| one | two | three | 
+---------+---------+----------+ 
| folder1 | detail1 | detail10 | 
| folder1 | detail2 | detail20 | 
| folder1 | detail3 | detail30 | 
| folder2 | detail1 | detail10 | 
| folder2 | detail2 | detail20 | 
| folder2 | detail3 | detail30 | 
| folder3 | detail1 | detail10 | 
| folder3 | detail2 | detail20 | 
| folder3 | detail3 | detail30 | 
| folder4 | detail1 | detail10 | 
| folder4 | detail3 | detail30 | 
+---------+---------+----------+ 
+0

Het @ SqlZim,第一个结果看起来完全符合我的要求,并且非常感谢你付出了很多努力来帮助我。我更喜欢看到旧表格的旧值,所以我可以自己查看错误,也可以在'diff'列中看到它们。在此先感谢 –

+0

我不确定你的意思,但如果像第三个例子那样将'full outer join'切换到'left join',那么结果将不包含'b'中的值在'a'中,如果这就是你的意思。 – SqlZim

+0

好的,谢谢,这似乎与我的具体表失败...:/ –

相关问题