图 | 榖依米
SQL Join 中,表位置对性能的影响
出这样一个话题,老读者估计要说我炒冷饭。
其实还真不是。两表的 Join, Internals(内幕)还是有很多可以讨论。比如 join 算法,Predicate 优化,Join 顺序对性能的影响,或者 DOP(degree of parallel).
今天我们谈最简单的一个,Join 中表顺序,对性能的影响。
经过前面 4 篇 Join 文章的论述,相信大家对于 Join 的算法已经不陌生了。至少知道三种基础 Join 算法的使用。比如 Nested Loop Join.
今天我们要讨论的还真是和 Nested Loop Join 算法有关。Nested Loop Join 有两种输入集,一是 Outer Input, 另一种是 Inner Input. 举个例子来看:
代码语言:javascript复制SELECT Per.BusinessEntityId,
Per.TerritoryId,
(
SELECT SUM(TaxAmt)
FROM SalesOrderHeader Ord
WHERE Ord.TerritoryId = Per.TerritoryId
)
FROM SalesPerson Per
WHERE Per.TerritoryId IS NOT NULL
ORDER BY Per.TerritoryId
千万别以为 Join 算法必须要有 Join 关键字,这里完全可以转为 LEFT JOIN, 但为了说明白 Join 算法的本质,就先不转换成大家平时常用的写法。有兴趣的朋友,可以自己改写。当然,是 Inner Join 还是 Left Join 就看大家的理解了。我估计还是有部分同学,会用错。
查看执行计划,本质上还是Join算法:
算法的重点来了,Nested Loop Join, 上面说到它有两个输入集,Outer Input 与 Inner Input. 在这里,与 NLJ 最近的两个分别是 Sort, Table Spool, 而本质上这两个输入集的最终源头是 SalesPerson 和 SalesOrderHeader.
那么为什么会选择 SalesPerson 作为 Outer Input,而 SalesOrderHeader 作为 Inner Input呢?其实答案很好解释,看下面的图,就明白:
(自己用ipadpro画的图,很有诚意吧,虽然字不好看)
SalesPerson 装的是销售员即人的数据,而SalesOrderHeader 则装的是销售订单数据。那么一个企业里面人肯定比订单数少的多。如果销售人数是100人,那么只要在 Inner Input 中执行 100 次就可以完成计算。而反过来,将订单表作为 Outer Input, 则需要把整张订单表做 Scan/Seek, 那么量级就相差很远。
由此可以推测,优化器选择执行计划时,一定程度上自动判断了两表大小,选择小表在前,大表在后的原则。小表驱动大表查询,是优化时着重考虑的策略。