为什么子查询使用并行性而联接不使用并行性?


16

为什么SQL Server在运行使用子查询的查询时使用并行性,而在使用联接时却不使用并行性?加入版本以串行方式运行,大约需要30倍的时间才能完成。

加入版本:〜30秒

在此处输入图片说明

子查询版本:<1秒

在此处输入图片说明

编辑: 查询计划的Xml版本:

加入版本

子查询版本

Answers:


12

正如评论中已指出的那样,您好像需要更新统计信息。

估计的行数来自两个计划之间的连接,location并且testruns在两个计划之间存在巨大差异。

加盟计划估算:1

方案1

子查询计划估算值:8,748

在此处输入图片说明

从联接中出来的实际行数为14,276。

当然,从绝对意义上讲,location联接版本应该估计应该来自3行并产生单个联接行,而子查询估计这些行中的单个会从同一联接中产生8,748,但是我仍然能够重现此。

如果创建统计数据时直方图之间没有交叉,则似乎会发生这种情况。联接版本假定为单行。子查询的单个相等搜索假定与针对未知变量的相等搜索假定相同的估计行。

测试运行的基数为26244。假设其中填充了三个不同的位置ID,则以下查询估计8,748将返回行(26244/3

declare @i int

SELECT *
FROM   testruns AS tr
WHERE  tr.location_id = @i

假设该表locations仅包含3行,则很容易(如果我们假设没有外键)会想到这样一种情况:创建统计信息,然后更改数据,从而极大地影响返回的实际行数,但不足以跳出统计信息的自动更新并重新编译阈值。

由于SQL Server从该联接中获得了行数,因此错误地估计了联接计划中的所有其他行估计。不仅意味着您获得了串行计划,查询还获得了不足的内存授权,并且排序和散列联接溢出到tempdb

下面是一种重现计划中显示的实际行与估计行的可能情况。

CREATE TABLE location
  (
     id       INT CONSTRAINT locationpk PRIMARY KEY,
     location VARCHAR(MAX) /*From the separate filter think you are using max?*/
  )

/*Temporary ids these will be updated later*/
INSERT INTO location
VALUES      (101, 'Coventry'),
            (102, 'Nottingham'),
            (103, 'Derby')

CREATE TABLE testruns
  (
     location_id INT
  )

CREATE CLUSTERED INDEX IX ON testruns(location_id)

/*Add in 26244 rows of data split over three ids*/
INSERT INTO testruns
SELECT TOP (5984) 1
FROM   master..spt_values v1, master..spt_values v2
UNION ALL
SELECT TOP (5984) 2
FROM   master..spt_values v1, master..spt_values v2
UNION ALL
SELECT TOP (14276) 3
FROM   master..spt_values v1, master..spt_values v2

/*Create statistics. The location_id histograms don't intersect at all*/
UPDATE STATISTICS location(locationpk) WITH FULLSCAN;    
UPDATE STATISTICS testruns(IX) WITH FULLSCAN;

/* UPDATE location.id. Three row update is below recompile threshold*/
UPDATE location
SET    id = id - 100

然后,运行以下查询可得出相同的估计值与实际差异

SELECT *
FROM   testruns AS tr
WHERE  tr.location_id = (SELECT id
                         FROM   location
                         WHERE  location = 'Derby')

SELECT *
FROM   testruns AS tr
       JOIN location loc
         ON tr.location_id = loc.id
WHERE  loc.location = ( 'Derby' ) 

如果在位置上添加了唯一约束,则很明显“ =”将仅返回一行。然后在您的示例中,查询计划变得相同(扫描->搜索):alter table位置添加约束U_Location_Location唯一非聚集(位置);
crokusek 2014年

@crokusek是的。意识到您之后的意思并删除了我的评论!这样是否也会增加联接版本的估计行数,使其与子查询相同?暂时不在PC上测试吗?
马丁·史密斯

@crokusek是的。在单例情况下,看起来与联接中的估计行相同。
马丁·史密斯

是。相同的查询计划,均估计为8748,均为实际14276。顺便说一句,我认为预先计算locationId可以解决该差异,但不能解决。
crokusek 2014年

1
@crokusek-我还将在数据库中的位置和其他类似位置添加唯一约束。我必须承认,我没有意识到它会影响查询优化。我认为这只是为了确保数据完整性。感谢您对这个问题的投入。
克里斯L
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.