解决方法
正如评论中已经指出的那样,您似乎需要更新统计信息.
从位置和测试之间的连接中出来的估计行数在两个计划之间是非常不同的.
加入计划估算:1
子查询计划估计:8,748
从连接中出来的实际行数是14,276.
当然,绝对没有直觉意味着连接版本应该估计3行应该来自位置并产生单个连接行,而子查询估计这些行中的单个行将从同一连接产生8,748但是我仍然是能够重现这一点.
如果在创建统计信息时直方图之间没有交叉,则似乎会发生这种情况.连接版本假定为单行.并且子查询的单个相等性查找假设与对未知变量的相等性搜索相同的估计行.
测试的基数是26244.假设有三个不同的位置ID,则以下查询估计将返回8,748行(26244/3)
- declare @i int
- SELECT *
- FROM testruns AS tr
- WHERE tr.location_id = @i
鉴于表位置只包含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' )