【转】SQLSERVER中如何快速比较两张表的不一样(包括自己的见解)

时间:2024-02-21 17:18:21

这里转一篇别的博主的随笔《SQLSERVER中如何快速比较两张表的不一样》。

 

这是文章的出处:http://www.cnblogs.com/lyhabc/p/4176269.html

 

关于这里的几种方法,这里我有些自己的看法。

这么多种方法,我个人觉得比较有用的是

 

EXCEPT  减法归零 -- 这种办法简单,一条SQL语句就可以解决。不过这里需要进一步考虑一个性能问题。EXCEPT方法其实在执行计划上得表现和EXISTS其实是一样的。过程中会产生一个数据集的排序。这个Sort的开销还是蛮大的,体现在对CPU和内存的使用上。所以当数据集大的情况下相信性能不会好。而且它局限于对本地两张表的对比。这也是一个局限性。

 

SQLSERVER自带的tablediff工具 -- 工具型办法。这个我不了解底层是怎么运作的。不过挺方便的。而且支持与远程表对比。前提是表的结构必须一致。

checksum校验 -- 准确来讲不是checksum校检,应该称为哈希校检。用HASHBYTES函数代替CHECKSUM函数,因为CHECKSUM函数其自身4个字节长度的INT值暴露了其自身容易出现生成校检值重复的风险。在保持参与HASH值计算的表字段保持相同的顺序,对表的数据行使用相同的HASH算法生成HASH值然后进行对比。这种方法也是我觉得最好的,在于远程表对比和性能的体现上。

性能和EXCEPT对比它的优势是不需要排序,表全扫描后产生HASH值在执行计划上以一个COMPUTE SCALAR操作符体现,消耗的是CPU计算。而对于内存的消耗上,因为数据扫描完产生HASH值就可以被PAGE OUT了(这是我的理解)。HASH值因为字节占用小所以对内存的占用应该不是性能的瓶颈。

很多情况需要和远程表进行一个数据对比。这种情况下对远端数据表生成HASH值后传输到本地与本地表的HASH值对比,节省中间的网络IO,对比过程也节省内存消耗。虽然HASHBYTES不支持*作为传入参数,而且最多只能支持4000字节长度的输入,但是可以通过一些办法来解决。比如对于TEXT类型,就转换成NVARCHAR(MAX),HASHBYTES是支持NVARCHAR(MAX)的。其次,对于NULL值替换成\'\'。然后把所有的字段串联起来,COL1+COL2+CAST(TEXT AS NVARCHAR(MAX))。如果串联后的字符长度超过了4000长度,就拆成多个HASH值。实际的应用场景比如ETL中没有办法得到LastUpdateDatetime,但是表太大,全表加载不合理。这个时候用这种办法就可以实现查找增量变化的那些数据行。

 

-------------------------------------------------------------------------------------------------------------------

SQLSERVER中如何快速比较两张表的不一样

一般来说,如何检测两张表的内容是否一致,体现在复制的时候发布端和订阅端的两端的数据上面

我这里罗列了一些如何从数据库层面来解决此类问题的方法


第一步当然就是检查记录数是否一致,否则不用想其他方法了~
这里我们用两张表t1_old,t1_new来演示


方法介绍

 

方法一:老老实实看表结构和表记录数,弊端是根本看不到两张表的数据是否一致,只是看到表结构和记录数是否一致

--表结构:
 CREATE TABLE t1_old (
  id int NOT NULL,
  log_time DATETIME DEFAULT \'\'
) ;
CREATE TABLE t1_new ( id int NOT NULL, log_time DATETIME DEFAULT \'\' ) ; --两表的记录数都为100条。 select count(*) from t1_old; select count(*) from t1_new;

 

 

方法二:加法去重 union 运算符排除重复的,但是有bug,在某些情形下不能简单表示结果集一致,相当于无效

由于Union 本身具备把上下两条连接的记录做唯一性排序,所以这样检测起来比较简单

SELECT  COUNT(*)
FROM    ( SELECT    *
          FROM      [t1_old]
          UNION
          SELECT    *
          FROM      [t1_new]
        ) AS T;


INSERT INTO [dbo].[t1_new]
        ( [id],[log_time] )
VALUES(1,\'\'),(3,\'\'),(4,\'\')

INSERT INTO [dbo].[t1_old]
        ( [id],[log_time] )
VALUES(1,\'\'),(2,\'\'),(3,\'\')

SELECT * FROM [dbo].[t1_new]
SELECT * FROM [dbo].[t1_old]

SELECT  COUNT(*)
FROM    ( SELECT    *
          FROM      [t1_new]
          UNION
          SELECT    *
          FROM      [t1_old]
        ) AS T;

两表数据

查询出来的结果是4

 

 

方法三:EXCEPT  减法归零

SELECT  COUNT(*)
FROM    ( SELECT    *
          FROM      [dbo].[t1_new]
          EXCEPT
          SELECT    *
          FROM      [dbo].[t1_old]
        ) AS T;

SELECT  COUNT(*)
FROM    ( SELECT    *
          FROM      [dbo].[t1_old]
          EXCEPT
          SELECT    *
          FROM      [dbo].[t1_new]
        ) AS T;

SELECT * FROM [dbo].[t1_new]

SELECT * FROM [dbo].[t1_old]

这里检测出来结果不对,那么就直接给出不一致的结论

 

 

方法四:用全表INNER JOIN,这个也是最烂的做法,当然这里指的是在表记录数超级多的情况下

DECLARE @t1_newcount BIGINT
DECLARE @count BIGINT


SELECT  @t1_newcount = COUNT(*)
FROM    t1_new;


SELECT  @count = COUNT(*)
FROM    [t1_old] AS a
        INNER JOIN [t1_new] AS b ON [b].[id] = [a].[id]
                                    AND [b].[log_time] = [a].[log_time] --如果表中还有其他字段的自行添加
PRINT @count
PRINT @t1_newcount
IF ( @count = @t1_newcount )
    BEGIN 
        SELECT  \'equal\'
    END 
ELSE
    BEGIN
        SELECT  \'not equal\'

    END 

 

 

方法五:借助SQLSERVER自带的tablediff工具,当初微软制作这个工具的目的就是用于比较复制中发布表和订阅表的数据一致

identical是相等的意思

 

方法六:借助发布端的验证订阅功能,验证订阅端跟发布端的数据是否一致

 

 

方法七:用checksum校验,比较两张表里的内容的checksum值是否一致

但是这种方法也只局限于两表结构一摸一样

我把[t1_new]表的数据复制到一张新的表以便进行比较

SELECT * FROM [dbo].[t1_new]
SELECT * FROM [dbo].[t1_newreplica]



SELECT SUM(CHECKSUM(*)) AS checksumvalue FROM [dbo].[t1_old]
SELECT SUM(CHECKSUM(*)) AS checksumvalue FROM [dbo].[t1_new]
SELECT SUM(CHECKSUM(*)) AS checksumvalue FROM [dbo].[t1_newreplica]


总结

从上面几种数据库提供的方法来看,用EXCEPT减法来归零相对来说比较可靠,其他的方法比较适合在特定的情形下来检测