Answers:
正如评论中已指出的那样,您好像需要更新统计信息。
估计的行数来自两个计划之间的连接,location
并且testruns
在两个计划之间存在巨大差异。
加盟计划估算: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' )