WHERE 谓词的顺序和 SQL 优化器

Posted

技术标签:

【中文标题】WHERE 谓词的顺序和 SQL 优化器【英文标题】:Order of WHERE predicates and the SQL optimizer 【发布时间】:2016-11-19 20:28:45 【问题描述】:

在编写带有各种where 子句的 SQL 查询时(我只使用 mysqlsqlite),我通常对重新排序查询子句以放置首先是“最好的”(将删除更多行的那些),然后是其他“修饰”子句(几乎不会改变输出)。换句话说,我怀疑我是否真的会通过重新排序子句来帮助优化器更快地运行(特别是当有索引在起作用时),或者它是否可能是另一种过早优化的情况。优化人员通常比我聪明。

例如:

select address.* from address inner join
                      user on address.user = user.id
where address.zip is not null and address.country == user.country

如果我们知道address.zip 通常不为空,则该检查将有 90% 为真,并且如果查询顺序得到遵守,则会有很多虚拟检查,可以通过放置 country 来避免检查之前。

我应该照顾它吗?换句话说,where 子句的顺序是否重要?

【问题讨论】:

您使用的是哪个数据库? ...因为您的问题不是独立于数据库的 @scaisEdge 在末尾添加了注释。 @DuduMarkovitz 很难听到。为什么听起来那么糟糕? (我的英语不太好,我很难知道我的问题听起来如何)。 @Peregring-lk,你的英语很好,但问题太模糊了。它应该像字典定义一样。如果主题中还没有,那么第一行的主要思想应该很清楚。不要讲故事。从主要思想开始,然后使用简单明了的示例进行深入研究。 @Peregring-lkq,查看有关 MySQL 的更新答案,但请仔细阅读上一节的末尾 【参考方案1】:

mysql 优化器似乎有据可查,您可以在官方文档中找到许多有趣的注意事项..http://dev.mysql.com/doc/refman/5.7/en/where-optimizations.html

特别要考虑一个非常简单的事实......已编写,但重要的是仅声明了哪些元素的事实。这在关于 mysql 优化的文档中很明显,其中重点仅放在查询的组件以及它们如何通过 optmizer 在内部组件中进行转换

【讨论】:

【参考方案2】:

顺序几乎无关紧要。

在 MySQL 中,使用WHERE ... AND ...

优化器将首先查找可以使用索引的部分。如果一个可以,一个不能,优化器将使用索引;顺序变得无关紧要 如果AND 的两边都可以使用索引,MySQL通常会选择“更好”的那个。 (有时它会出错。)同样,订单被忽略了。 如果双方都不能使用索引,它会从左到右进行评估。但是...获取行是执行查询的大部分工作,因此如果AND 的一侧比另一侧慢一点,您可能不会注意到。 (当然,如果一侧是SLEEP(3),你会注意到的。)

您的示例查询中还有另一个问题(除了语法错误):优化器将有意识地决定从哪个表开始。

如果它决定以user 开头,address 需要INDEX(user, country) 以任一顺序。 如果它决定以address 开头,则user 需要(id, country) 的任一顺序。 尚不清楚优化器是否会打扰 NOT NULL 测试,即使该列已编入索引。

底线:花时间专注于optimal indexes

【讨论】:

【参考方案3】:

答案肯定是可能的。 神秘的是优化器的方式。

这里是一个基于被零除引起的异常的演示。

create table t (i int);
insert into t (i) values (0);

Oracle、SQL Server、PostgresTeradata 的以下查询成功(我们现在将跳过版本信息):

select 1 from t where i < 1 or 1/i < 1;

SQL ServerPostgres 的以下查询失败,但 OracleTeradata

的查询成功
select 1 from t where 1/i < 1 or i < 1;

但是,对于 OracleTeradata,以下查询确实会失败:

select 1 from t where 1/i < 1 or i/1 < 1;

我们学到了什么? 一些优化器似乎尊重谓词的顺序(或至少以某种方式),而另一些优化器似乎按其估计成本重新排序谓词(例如,1/i &lt; 1i &lt; 1 更昂贵,但不是 i/1 &lt; 1)。 对于那些尊重谓词顺序的人,我们可能可以通过将轻等待谓词放在 OR 运算符和经常错误的谓词放在 AND 运算符来提高性能。 话虽如此,由于数据库不能保证保留谓词的顺序,即使其中一些目前似乎这样做,你绝对不能指望它。


MySQL 5.7.11

此查询立即返回:

select 1 from t where i < 1 or sleep(3);

此查询在 3 秒后返回:

select 1 from t where sleep(3) or i < 1

【讨论】:

以上是关于WHERE 谓词的顺序和 SQL 优化器的主要内容,如果未能解决你的问题,请参考以下文章

SQL where 条件顺序对性能的影响有哪些

Oracle初级优化sql

如何优化SQL语句

1025WHERE执行顺序以及MySQL查询优化器

Oracle查询转换之连接谓词推入

使用 WHERE 子句中的过滤器优化 OUTER JOIN 查询。(查询规划器)