SQL 性能 UNION 与 OR
Posted
技术标签:
【中文标题】SQL 性能 UNION 与 OR【英文标题】:SQL Performance UNION vs OR 【发布时间】:2012-11-24 21:17:10 【问题描述】:我刚刚阅读了一篇优化文章的一部分,并segfaulted对以下语句:
在使用
OR
和UNION
的SQL 替换语句时:select username from users where company = ‘bbc’ or company = ‘itv’;
到:
select username from users where company = ‘bbc’ union select username from users where company = ‘itv’;
来自快速EXPLAIN
:
使用OR
:
使用UNION
:
这不意味着UNION
做了双倍的工作?
虽然我很欣赏 UNION
对于某些 RDBMS 和某些表模式可能更高效,但正如作者建议的那样,这绝对正确。
问题
我错了吗?
【问题讨论】:
我认为UNION
正在做更多的工作,因为它需要删除重复项,过滤器将获取指定的条件。我很惊讶它不推荐使用IN
。
如果WHERE
子句中有OR
时mysql 不能使用索引,这可能是真的。
@Igor Romanchenko,用答案支持这个。
我猜查询分析器会将 or 语句重写为 IN 语句,因为它们是等价的
@Darren Kopp,我很确定情况正好相反。至少在 MySQL 中。
【参考方案1】:
要么你阅读的文章使用了一个不好的例子,要么你误解了他们的观点。
select username from users where company = 'bbc' or company = 'itv';
这相当于:
select username from users where company IN ('bbc', 'itv');
MySQL 可以使用company
上的索引来进行此查询。无需执行任何 UNION。
更棘手的情况是您有一个涉及两个不同列的OR
条件。
select username from users where company = 'bbc' or city = 'London';
假设company
上有一个索引,city
上有一个单独的索引。鉴于 MySQL 通常在给定查询中每个表只使用一个索引,它应该使用哪个索引?如果它使用company
上的索引,它仍然需要进行表扫描才能找到city
是伦敦的行。如果它使用city
上的索引,则必须对company
为bbc 的行进行表扫描。
UNION
解决方案适用于这种情况。
select username from users where company = 'bbc'
union
select username from users where city = 'London';
现在每个子查询都可以使用索引进行搜索,子查询的结果由UNION
组合而成。
一位匿名用户提议对我上面的回答进行修改,但版主拒绝了该修改。它应该是评论,而不是编辑。提议的编辑声称 UNION 必须对结果集进行排序以消除重复行。这使得查询运行速度变慢,因此索引优化是一种清洗。
我的回答是索引有助于在 UNION 发生之前将结果集减少到少量行。 UNION 实际上确实消除了重复,但要做到这一点,它只需要对小的结果集进行排序。在某些情况下,WHERE 子句与表的很大一部分匹配,并且在 UNION 期间进行排序与简单地进行表扫描一样昂贵。但更常见的是通过索引搜索减少结果集,因此排序的成本远低于表扫描。
差异取决于表中的数据以及正在搜索的字词。确定给定查询的最佳解决方案的唯一方法是尝试the MySQL query profiler 中的两种方法并比较它们的性能。
【讨论】:
我提供的报价是文章中的确切示例。所以没有什么可误解的。我知道使用UNION
与OR
是绝对不正确。但我将其标记为正确,因为它将原始示例处理为不正确,同时提供了作者可能意思的用例。
唉,作者可能一直在写解决方案,却不了解解决方案有用与不需要的情况。或者,他可能将他的知识建立在一个没有优化 IN()
谓词的古老版本的 MySQL 上。
@BillKarwin 如果两个不同的列都被索引了,那么 MySQL 不会执行“索引合并优化”来获得基于两个索引的单个扫描的合并结果吗?
我终于明白UNION的必要性了。谢谢!我正在从亚马逊订购你的书。
这解释了很多,特别是在 Where 子句中验证来自一个联接的列或来自另一个联接的另一列时的查询时间。谢谢!【参考方案2】:
这些不是同一个查询。
我对 MySQL 没有太多经验,所以我不确定查询优化器做什么或不做什么,但这里是我的一般背景(主要是 ms sql server)的想法。
通常,查询分析器可以获取上述两个查询并从中制定完全相同的计划(如果它们相同的话),所以这无关紧要。我怀疑这些查询之间没有性能差异(它们是等效的)
select distinct username from users where company = ‘bbc’ or company = ‘itv’;
和
select username from users where company = ‘bbc’
union
select username from users where company = ‘itv’;
现在的问题是,以下查询之间是否存在差异,我实际上不知道,但我怀疑优化器会使它更像第一个查询
select username from users where company = ‘bbc’ or company = ‘itv’;
和
select username from users where company = ‘bbc’
union all
select username from users where company = ‘itv’;
【讨论】:
+ 关于查询不一样。尽管如此,UNION ALL
仍然产生与UNION
相同的EXPLAIN
。
UNION ALL
通常比UNION
快。后者暗示UNION DISTINCT
,因此需要对临时表进行重复数据删除。较新的版本在某些情况下避免使用临时表,从而提供更多帮助。您拥有的or
示例总是更快,因为它可以使用INDEX(company)
【参考方案3】:
这取决于优化器最终根据数据大小、索引、软件版本等执行的操作。
我猜想使用 OR 将使优化器更有机会找到一些效率,因为一切都在一个单一的逻辑语句中。
此外,UNION 有一些开销,因为它创建了一个重置 set(没有重复)。 如果 company 被索引,则 UNION 中的每个语句都应该很快执行......不确定它是否真的在做 double 的工作。
底线
除非您真的迫切需要从查询中提取所有速度,否则最好使用最能传达您的意图的表单...... OR
更新
我还想说 IN。我相信以下查询将提供比 OR 更好的性能(这也是我更喜欢的形式):
select username from users where company in ('bbc', 'itv');
【讨论】:
【参考方案4】:在几乎所有情况下,union
或union all
版本都会对用户表进行两次全表扫描。
or
版本在实践中要好得多,因为它只会扫描表一次。如果可用,它还将只使用一次索引。
对于任何数据库和任何情况,原始陈述似乎都是错误的。
【讨论】:
为了清楚起见,UNION
也将使用索引(如果可用)。但它会扫描两个表。只是一个较小的数据集,然后将它们重新合并在一起。
no-no-no,如果您使用or
/in
,您将使用“索引范围扫描”,而对于union
/union all
,您将使用使用non-unique
甚至primary key lookup
加上index merge
@YevgeniyAfanasyev 。 . .解释结果很清楚,表上没有索引。
感谢您的评论。问题并没有说明“表上没有索引”的条件以及您的回答。如果您将此放在答案的开头,它将帮助像我这样的人寻找他们的案例场景。
IN
和 OR
是相同的。您可以通过获取EXPLAIN
来查看这一点,以查看优化器将一个转换为另一个。【参考方案5】:
Bill Karwin 的回答非常正确。当 OR 语句的两个部分都有自己的索引时,最好进行联合,因为一旦你有一小部分结果,就更容易对它们进行排序并消除重复项。总成本几乎比只使用一个索引(对于其中一列)和对另一列进行表扫描(因为 mysql 只对一列使用一个索引)要少。
这通常取决于表的结构和需求,但在大表中联合给了我更好的结果。
【讨论】:
你说mysql only uses one index for one column
- 这不是真的。您可以将列放在许多索引中。
在执行查询期间,mysql 对单个列仅使用一个索引。这与在一列上定义多个索引的能力无关。
这又不是真的。 MySQL 使用一个索引进行查询,而不是一列。
让我们更准确地说:“每个SELECT
一个索引”。 (这避免了 UNION
是一个还是多个“查询”的歧义。)
@RickJames 我说“每个表引用一个索引”,因为在带有连接的查询中,每个表引用都可能使用一个索引。即使在进行自联接时,对同一个表的每个表引用也可能使用不同的索引。以上是关于SQL 性能 UNION 与 OR的主要内容,如果未能解决你的问题,请参考以下文章