Transact-SQL-子查询还是左联接?
我有两个包含"任务"和"注释"的表,并且想要检索一个任务列表,以及每个任务的关联注释数。这两个查询可以完成任务:
select t.TaskId, (select count(n.TaskNoteId) from TaskNote n where n.TaskId = t.TaskId) 'Notes' from Task t -- or select t.TaskId, count(n.TaskNoteId) 'Notes' from Task t left join TaskNote n on t.TaskId = n.TaskId group by t.TaskId
它们之间有区别吗?我应该在一个之上使用另一个,还是它们只是完成同一工作的两种方式?谢谢。
解决方案
我们可以使用任何一种,并且它们在语义上是相同的。通常,经验法则是使用任何一种我们更容易阅读的形式,除非性能是一个问题。
如果性能是一个问题,请尝试使用另一种形式重写查询。有时,优化器会为一种形式而不是另一种形式使用索引。
没有明确的答案。我们应该查看SQL计划。就关系代数而言,它们本质上是等效的。
在小型数据集上,当涉及到性能时,它们会被淘汰。索引时,LOJ会好一些。
我发现在大型数据集上,内部联接(内部联接也将起作用。)在很大的因素(抱歉,没有数字)方面优于子查询。
在大多数情况下,优化器将对它们进行相同的处理。
我倾向于第二种,因为它的嵌套较少,这使得它更易于阅读和维护。由于相同的原因,我也开始使用SQL Server的公用表表达式来减少嵌套。
另外,如果将来除了COUNT以外可能还会添加其他汇总,则第二种语法会更加灵活,例如MIN(some_scalar),MAX(),AVG()等。
如果使用的是SQL Server Management Studio,则可以将两个版本输入到"查询编辑器"中,然后右键单击并选择"显示估计的执行计划"。相对于批次,它将给我们两个百分比的成本。如果预计它们会花费相同的时间,则在两种情况下它们都将显示为50%,出于其他原因(易于阅读,易于维护,更符合编码标准等),选择我们喜欢的任何一个。否则,我们可以选择相对于批次成本较低的一种。
我们可以使用相同的技术通过比较两个执行相同操作的版本来查看更改任何查询以提高性能。
当然,因为这是相对于批次的成本,所以这并不意味着任何一个查询都尽可能快,它只是告诉我们它们如何相互比较,而不是告诉我们一些概念上的最佳查询以获得相同的结果。
子查询将变慢,因为它正在外部查询中的每一行执行。一旦完成,联接将更快。我相信查询优化程序不会重写此查询计划,因为它无法识别等效项。
通常,我们需要进行联接和分组以进行这种计数。如果我们显示的相关子查询必须在不参与另一个联接的表上进行某些分组或者更复杂的谓词,则这些子查询将是我们最感兴趣的子查询。
我将重点放在尽可能避免子查询上。连接通常会更有效。