2

我正在使用 SQL Server 2012 LocalDB。

一位客户希望我比较 2 个表,这些表包含 200 个(!)值列中的每一个(信不信由你)nvarchar(max)。没有索引,也没有唯一键。

我用谷歌搜索了我的方法(http://weblogs.sqlteam.com/jeffs/archive/2004/11/10/2737.aspx)并且该方法有效。但是,在 union all / group by 表达式中使用 200 列有点慢。

查询看起来像这样

SELECT 
    MIN(TableName) as TableName , header1, header2, header3, header....
INTO RESULTS  
FROM 
    (SELECT 
        'table1' as TableName, table1.header1, table1.header2, table1.header3, table1.header...
     FROM table1 

     UNION ALL 

     SELECT 
         'table2' as TableName , table2.header1, table2.header2, table2.header3, table2.header...
    ) tmp 
GROUP BY 
    header1, header2, header3, header...
HAVING 
    COUNT(*) = 1

第一次查询的执行计划

我的想法是计算此处描述的任何行的哈希字节值(http://www.bidn.com/blogs/TomLannen/bidn-blog/2265/using-hashbytes-to-compare-columns)并将其保存在额外的列(在这种情况下再增加一个不会受到伤害:-))。哈希字节本身的计算速度非常快,但是当我之后仅使用 union all / group by /having 子句中的哈希字节值运行查询时,查询速度要慢得多。在第一种情况下,它运行大约 15 分钟,而不是大约 25 秒!

第二个查询如下所示(compareHash 是插入的 hashbyte 列):

SELECT 
    MIN(TableName) as TableName, compareHash 
INTO RESULTS  
FROM 
   (SELECT 
       'table1' as TableName , compareHash 
    FROM table1 

    UNION ALL 

    SELECT 
       'table2' as TableName , compareHash 
    FROM table2) tmp 
GROUP BY 
    compareHash 
HAVING 
    COUNT(*) = 1

第二次查询的执行计划 我本来期望完全相反。任何人都可以考虑这种行为的原因吗?

最好的问候塞巴斯蒂安

4

1 回答 1

0

如果查看查询计划,您会注意到第二个查询返回的行数明显减少(根据箭头的粗细判断)。

这让我想知道您是否正确填充了该compareHash列。即,如果所有 200 列都正确包含在哈希中。

如果有的话,我希望第二个查询返回更多行,因为散列字符数据将强制比较区分大小写。sql server 中的默认排序规则将为您对 varchar 值的第一个查询执行不区分大小写的比较。

虽然这更多地与正确性有关,但您通常希望在比较性能时比较产生相同答案的方法。


除了正确性之外,另一个观察结果是,在第一个查询中,数据被排序,然后应用流式聚合。在第二个查询中,构建了一个哈希表,并使用第二个表来探测哈希表。您可以尝试通过在查询OPTION (ORDER GROUP)末尾添加并查看是否会更改执行时间来强制第二个查询中的排序流聚合计划,但我怀疑它会改善它。

我的猜测是原始 varchar 数据具有一些自然顺序,sql server 在第一个查询中利用了这些顺序。

如果您愿意在compareHash列上创建索引,您可能会在第二个查询中看到显着的性能提升,因为它将使用合并连接。

于 2012-10-09T21:48:39.393 回答