sql-server – 为什么子查询使用并行性而不是连接?

前端之家收集整理的这篇文章主要介绍了sql-server – 为什么子查询使用并行性而不是连接?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
为什么sql Server在运行使用子查询查询时会使用并行性,但在使用连接时却不然?连接版本以串行方式运行,完成时间大约需要30倍.

加入版本:~30secs

查询版本:< 1秒 编辑:
Xml版本的查询计划:

JOIN version

SUBQUERY version

解决方法

正如评论中已经指出的那样,您似乎需要更新统计信息.

从位置和测试之间的连接中出来的估计行数在两个计划之间是非常不同的.

加入计划估算:1

查询计划估计:8,748

从连接中出来的实际行数是14,276.

当然,绝对没有直觉意味着连接版本应该估计3行应该来自位置并产生单个连接行,而子查询估计这些行中的单个行将从同一连接产生8,748但是我仍然是能够重现这一点.

如果在创建统计信息时直方图之间没有交叉,则似乎会发生这种情况.连接版本假定为单行.并且子查询的单个相等性查找假设与对未知变量的相等性搜索相同的估计行.

测试的基数是26244.假设有三个不同的位置ID,则以下查询估计将返回8,748行(26244/3)

  1. declare @i int
  2.  
  3. SELECT *
  4. FROM testruns AS tr
  5. WHERE tr.location_id = @i

鉴于表位置只包含3行,很容易(如果我们假设没有外键)来设计创建统计信息的情况,然后以显着影响返回的实际行数但不足的方式更改数据跳转统计信息的自动更新并重新编译阈值.

由于sql Server获取了来自该连接的行数所以错误,因此连接计划中的所有其他行估计都被大大低估了.除了意味着您获得了一个串行计划之外,查询还获得了不足的内存授权,并且排序和散列连接溢出到tempdb.

重现计划中显示的实际行与估计行的一种可能方案如下.

  1. CREATE TABLE location
  2. (
  3. id INT CONSTRAINT locationpk PRIMARY KEY,location VARCHAR(MAX) /*From the separate filter think you are using max?*/
  4. )
  5.  
  6. /*Temporary ids these will be updated later*/
  7. INSERT INTO location
  8. VALUES (101,'Coventry'),(102,'Nottingham'),(103,'Derby')
  9.  
  10. CREATE TABLE testruns
  11. (
  12. location_id INT
  13. )
  14.  
  15. CREATE CLUSTERED INDEX IX ON testruns(location_id)
  16.  
  17. /*Add in 26244 rows of data split over three ids*/
  18. INSERT INTO testruns
  19. SELECT TOP (5984) 1
  20. FROM master..spt_values v1,master..spt_values v2
  21. UNION ALL
  22. SELECT TOP (5984) 2
  23. FROM master..spt_values v1,master..spt_values v2
  24. UNION ALL
  25. SELECT TOP (14276) 3
  26. FROM master..spt_values v1,master..spt_values v2
  27.  
  28. /*Create statistics. The location_id histograms don't intersect at all*/
  29. UPDATE STATISTICS location(locationpk) WITH FULLSCAN;
  30. UPDATE STATISTICS testruns(IX) WITH FULLSCAN;
  31.  
  32. /* UPDATE location.id. Three row update is below recompile threshold*/
  33. UPDATE location
  34. SET id = id - 100

然后运行以下查询给出相同的估计与实际差异

  1. SELECT *
  2. FROM testruns AS tr
  3. WHERE tr.location_id = (SELECT id
  4. FROM location
  5. WHERE location = 'Derby')
  6.  
  7. SELECT *
  8. FROM testruns AS tr
  9. JOIN location loc
  10. ON tr.location_id = loc.id
  11. WHERE loc.location = ( 'Derby' )

猜你在找的MsSQL相关文章