假设我有以下查询:
SELECT Id, Name, ForeignKeyId,
(SELECT TOP (1) FtName FROM ForeignTable WHERE FtId = ForeignKeyId)
FROM Table
如果使用JOIN编写该查询会更快执行:
SELECT Id, Name, ForeignKeyId, FtName
FROM Table t
LEFT OUTER JOIN ForeignTable ft
ON ft.FtId = t.ForeignTableIf
只是好奇......另外,如果JOIN更快,在所有情况下(包含大量列,大量行的表)会更快吗?
编辑:我写的查询仅用于说明TOP(1)与JOIN的概念。是的 - 我知道SQL Server中的查询执行计划,但我不打算优化单个查询 - 我试图理解SELECT TOP(1)与JOIN背后是否存在某些理论,以及是否因为速度而选择某种方法(不是因为个人偏好或可读性)。
EDIT2:我要感谢Aaron的详细解答,并鼓励人们查看他在答案中提到的公司SQL Sentry Plan Explorer免费工具。
答案 0 :(得分:12)
最初,我写道:
查询的第一个版本对我来说不太可读。特别 因为你不打扰别名里面的匹配列 相关子查询。 JOIN很多很多更清楚。
我仍然相信并支持这些陈述,但我想根据添加到问题中的新信息添加我原来的回复。你问过,是否存在关于什么表现更好的一般规则或理论,TOP(1)或JOIN,将可读性和偏好放在一边)?我会重新陈述,因为我评论说不,没有一般规则或理论。当您拥有特定的示例时,很容易证明什么效果更好。让我们看看这两个类似于你的查询,但这些查询针对我们都可以验证的系统对象运行:
-- query 1:
SELECT name,
(SELECT TOP (1) [object_id]
FROM sys.all_sql_modules
WHERE [object_id] = o.[object_id]
)
FROM sys.all_objects AS o;
-- query 2:
SELECT o.name, m.[object_id]
FROM sys.all_objects AS o
LEFT OUTER JOIN sys.all_sql_modules AS m
ON o.[object_id] = m.[object_id];
这些返回完全相同的结果(我的系统上有3,179行),但是我指的是相同的数据和相同的行数。他们不是真正相同的查询(或者至少不遵循相同的执行计划)的一个线索是结果以不同的顺序返回。虽然我不希望维护或遵守某个命令,因为我没有在任何地方包含ORDER BY
,我希望SQL Server选择相同的顺序,如果它们实际上是使用相同的计划。
但他们不是。我们可以通过检查计划并进行比较来看到这一点。在这种情况下,我将使用SQL Sentry Plan Explorer,一个来自我公司的免费执行计划分析工具 - 您可以从Management Studio获取一些此类信息,但其他部分在Plan Explorer中更容易获得(例如实际持续时间和CPU)。顶部计划是子查询版本,底部是连接。同样,子查询位于顶部,连接位于底部:
实际执行计划:运行两个查询的总成本的85%是在子查询版本中。这意味着它的价格是加入价格的5倍多。子查询版本的CPU和I / O都要高得多 - 看看所有这些读取! 6,600多页返回~3,000行,而连接版本使用少得多的I / O返回数据 - 仅110页。
但为什么呢?因为子查询版本的工作方式基本上类似于标量函数,您可以从另一个表中获取TOP匹配行,但是在原始查询中为每一行执行此操作。我们可以通过查看Top Operations选项卡看到该操作发生了3,179次,该选项卡显示了每个操作的执行次数。再次,更昂贵的子查询版本位于顶部,加入版本如下:
我会为您提供更彻底的分析,但总的来说,优化器知道它在做什么。陈述你的意图(这些表之间的这种类型的连接)和99%的时间它将自己解决什么是最好的基本方式(例如执行计划)。如果你试图超越优化器,请记住,你正在冒险进入相当先进的领域。
每条规则都有例外,但在这种特定情况下,子查询肯定是个坏主意。这是否意味着第一个查询中提出的语法总是一个坏主意?绝对不。可能存在一些模糊的情况,其中子查询版本与连接一样有效。我不能认为子查询有很多工作更好。因此,我会更倾向于那些更有可能更好或更好和更具可读性的那一方。我认为子查询版本没有任何优势,即使你发现它更具可读性,因为它很可能会导致更糟糕的性能。
一般情况下,我强烈建议您坚持使用更具可读性,自我记录的语法,除非您发现优化程序没有正确执行的情况(我敢打赌99%的情况下问题是糟糕的统计数据)或参数嗅探,而不是查询语法问题)。我怀疑,除了这些情况之外,你可以重现哪些复制品,哪些复杂的查询比它们更直接和逻辑等价的工作更好,这种情况很少见。您尝试查找这些案例的动机应该与您对普遍接受的“最佳实践”语法的非直觉语法的偏好大致相同。
答案 1 :(得分:0)
您的查询会执行不同的操作。第一种更类似于LEFT OUTER JOIN。
这取决于您的索引是如何设置性能的。但是JOIN更清楚。
答案 2 :(得分:0)
我同意上述声明(里克)。在执行计划中运行此...您将得到一个明确的答案。不需要猜测。
我同意Daniel和Davide的看法,这是两个不同的SQL语句。如果ForeignTable有多个具有相同FtId值的记录,那么您将获得重复的数据。假设第一个SQL语句是正确的,你将不得不用一些GROUP BY子句重写第二个。