是什么使 SQL 语句可优化?

What makes a SQL statement sargable?

提问人:DForck42 提问时间:4/29/2009 最后编辑:Matt Johnson-PintDForck42 更新时间:5/12/2021 访问量:98244

问:

根据定义(至少从我所看到的情况来看),sargable 意味着查询能够让查询引擎优化查询使用的执行计划。我试着查找答案,但似乎没有太多关于这个主题的内容。那么问题来了,什么可以使 SQL 查询可优化,或者不能使 SQL 查询可优化?任何文件将不胜感激。

供参考:Sargable

SQL Server 性能

评论

73赞 BFree 4/29/2009
+1 表示“可优化”。这是我今天的一句话。8-2
45赞 marc_s 4/29/2009
SARG = 搜索 ARGument。有趣的是:“SARG”在德语中的意思是“棺材”,所以当人们谈论 SARGABLE 时,我总是不得不微笑——能够放在棺材里吗?:-)
0赞 Frank Farmer 6/12/2010
可优化性取决于您的环境。MySQL的文档记录在这里:dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html
1赞 Hoagie 4/29/2009
我还可以补充 Adam 的回答,即在大多数情况下,堆积如山的信息对于每个数据库引擎都非常特殊。
0赞 Reversed Engineer 5/14/2018
使用自由文本字段而不是“查找表”也违背了使查询可优化的精神。用户在输入自由文本(例如城镇名称)时会拼写错误,而查找表会强制用户选择拼写正确的条目。值得一点额外的麻烦,因为这可以被正确地索引,而不是在谓词中使用 LIKE '%...%'。

答:

95赞 beach 4/29/2009 #1

别这样:

WHERE Field LIKE '%blah%'

这会导致表/索引扫描,因为 LIKE 值以通配符开头。

别这样:

WHERE FUNCTION(Field) = 'BLAH'

这会导致表/索引扫描。

数据库服务器必须根据表中的每一行计算 FUNCTION(),然后将其与 'BLAH' 进行比较。

如果可能,请反向执行:

WHERE Field = INVERSE_FUNCTION('BLAH')

这将对参数运行 INVERSE_FUNCTION() 一次,并且仍然允许使用索引。

评论

6赞 Adam Robinson 4/29/2009
您翻转函数的建议实际上只有在函数往返数据(意味着 f(f(n)) = n)时才有效。
6赞 beach 4/29/2009
真。我考虑过添加INVERSE_FUNCTION但不想混淆。我会改变它。
308赞 BradC 4/29/2009 #2

使查询不可优化的最常见方法是在 where 子句的函数内包含一个字段:

SELECT ... FROM ...
WHERE Year(myDate) = 2008

SQL 优化器不能对 myDate 使用索引,即使存在索引也是如此。从字面上看,它必须为表的每一行计算此函数。使用起来要好得多:

WHERE myDate >= '01-01-2008' AND myDate < '01-01-2009'

其他一些例子:

Bad: Select ... WHERE isNull(FullName,'Ed Jones') = 'Ed Jones'
Fixed: Select ... WHERE ((FullName = 'Ed Jones') OR (FullName IS NULL))

Bad: Select ... WHERE SUBSTRING(DealerName,4) = 'Ford'
Fixed: Select ... WHERE DealerName Like 'Ford%'

Bad: Select ... WHERE DateDiff(mm,OrderDate,GetDate()) >= 30
Fixed: Select ... WHERE OrderDate < DateAdd(mm,-30,GetDate()) 

评论

7赞 Mike Bailey 6/22/2012
在 of 中包含函数会导致查询变得不可优化吗?GROUP BY
2赞 Craig Tullis 3/27/2014
一些数据库引擎(Oracle、PostgreSQL)支持表达式索引,知道吗?
3赞 High Plains Grifter 11/9/2015
会不会有更好的版本?曾经有一位优化人员告诉我,在 where 子句中使用 OR 可以取消查询..?WHERE ((FullName = 'Ed Jones') OR (FullName IS NULL))SELECT... FROM ... WHERE FullName = 'Ed Jones' UNION SELECT...FROM...WHERE FullName IS NULL
3赞 Devin Lamothe 5/25/2018
@HighPlainsGrifter您应该在该查询上使用 UNION ALL - union 具有隐式 distinct,这使得查询的成本比您必须互斥数据集时所需的成本要高得多
2赞 CEGRD 9/19/2018
@BradC 在 MSSQL 2016 中,和 之间没有执行计划差异。他们都使用 FullName 上的索引并执行索引查找。Select ... WHERE isNull(FullName,'Ed Jones') = 'Ed Jones'Select ... WHERE ((FullName = 'Ed Jones') OR (FullName IS NULL))
12赞 Dries Van Hansewijck 4/29/2009 #3

在这个答案中,我假设数据库有足够的覆盖索引。关于这个话题的问题已经够多了。

很多时候,查询的可优化性是由相关索引的临界点决定的。临界点定义了在将一个表或结果集联接到另一个表或结果集时查找和扫描索引之间的区别。当然,一次搜索比扫描整个表要快得多,但是当您必须查找大量行时,扫描可能更有意义。

因此,除其他事项外,当优化程序期望一个表的结果行数小于下一个表上可能索引的临界点时,SQL 语句更易于优化。

您可以在此处找到详细的帖子和示例。

5赞 user2011845 6/10/2017 #4

要使操作被视为可优化,仅能够使用现有索引是不够的。在上面的示例中,在 where 子句中添加针对索引列的函数调用,仍然很可能会利用定义的索引。它将“扫描”,即从该列(索引)中检索所有值,然后消除与提供的筛选器值不匹配的值。对于行数较多的表,它仍然不够高效。 真正定义可优化性的是使用二进制搜索方法遍历 b 树索引的查询能力,该方法依赖于排序项目数组的半集消除。在 SQL 中,它将在执行计划中显示为“索引查找”。