SQL WHERE子句中的IN vs OR


Answers:


170

我假设您想知道以下两者之间的性能差异:

WHERE foo IN ('a', 'b', 'c')
WHERE foo = 'a' OR foo = 'b' OR foo = 'c'

根据MySQL手册,如果值是常数IN,则对列表进行排序,然后使用二进制搜索。我可以想象一下,OR它们没有特定的顺序进行逐一评估。因此IN在某些情况下更快。

最好的了解方法是使用特定数据在数据库中同时对它们进行概要分析,以查看哪种方法更快。

我在具有1000000行的MySQL上都尝试过。对列进行索引后,性能没有明显的区别-两者几乎都是即时的。当该列未建立索引时,我得到了以下结果:

SELECT COUNT(*) FROM t_inner WHERE val IN (1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000);
1 row fetched in 0.0032 (1.2679 seconds)

SELECT COUNT(*) FROM t_inner WHERE val = 1000 OR val = 2000 OR val = 3000 OR val = 4000 OR val = 5000 OR val = 6000 OR val = 7000 OR val = 8000 OR val = 9000;
1 row fetched in 0.0026 (1.7385 seconds)

因此,在这种情况下,使用OR的方法要慢30%左右。添加更多术语会使差异更大。结果可能在其他数据库和其他数据上有所不同。


20
如果优化程序值得其盐,则它们应该执行相同的操作。
Janick Bernet 2010年

27
@inflagranti:不幸的是,没有优化器是完美的。优化器是极其复杂的程序,每个实现都有其优点和缺点。这就是为什么我说您应该概述特定的实现。我以为该IN方法的额外结构比一堆可能的相关OR子句更易于优化。如果有一个OR方法更快的引擎,我会感到惊讶,但是有时候OR会更慢,我并不感到惊讶。
Mark Byers 2010年

2
@MarkByers优化器是否总不能将多个OR替换为IN
tymtam '16

36

找出答案的最佳方法是查看执行计划。


我在Oracle上尝试过,并且完全一样。

CREATE TABLE performance_test AS ( SELECT * FROM dba_objects );

SELECT * FROM performance_test
WHERE object_name IN ('DBMS_STANDARD', 'DBMS_REGISTRY', 'DBMS_LOB' );

即使查询使用了IN,执行计划也说它使用OR

--------------------------------------------------------------------------------------    
| Id  | Operation         | Name             | Rows  | Bytes | Cost (%CPU)| Time     |    
--------------------------------------------------------------------------------------    
|   0 | SELECT STATEMENT  |                  |     8 |  1416 |   163   (2)| 00:00:02 |    
|*  1 |  TABLE ACCESS FULL| PERFORMANCE_TEST |     8 |  1416 |   163   (2)| 00:00:02 |    
--------------------------------------------------------------------------------------    

Predicate Information (identified by operation id):                                       
---------------------------------------------------                                       

   1 - filter("OBJECT_NAME"='DBMS_LOB' OR "OBJECT_NAME"='DBMS_REGISTRY' OR                
              "OBJECT_NAME"='DBMS_STANDARD')                                              

1
如果您要测试的值超过3个,在Oracle中会发生什么?您是否知道Oracle无法执行与MySQL相同的二进制搜索优化,还是在两种情况下都可以执行它?
Mark Byers 2010年

2
@Mark Byers:我尝试使用10个值进行相同的查询,结果仍然相同。请注意,优化器按字母顺序重新排序了我的值。如果Oracle对过滤器进行了一些内部优化,我不会感到惊讶...
Peter Lang

5
Oracle还有一个INLIST ITERATOR操作,如果有可以使用的索引,它将选择该操作。不过,当我尝试过了,双方INOR用相同的执行计划结束。
Cheran Shunmugavel

7

OR运算符需要比IN构造复杂得多的评估过程,因为它允许许多条件,不仅像IN一样。

这类似于可以与OR一起使用但与IN不兼容的事物:更大。大于或等于,小于,小于或等于LIKE以及更多类似oracle REGEXP_LIKE的对象。此外,请考虑条件可能并不总是比较相同的值。

对于查询优化器,管理IN运算符更容易,因为它只是一个在多个条件下定义OR运算符且=值相同的构造。如果您使用OR运算符,则优化器可能不会认为您始终在相同的值上使用=运算符,并且,如果它没有执行更深,更复杂的阐述,则可能会排除仅存在=在所有涉及的条件下都使用相同值的运算符,因此排除了已提到的二进制搜索之类的优化搜索方法。

[编辑]优化器可能未实现优化的IN评估过程,但这并不排除可能会发生一次(数据库版本升级)。因此,如果使用OR运算符,则在您的情况下将不使用优化的详细说明。


6

我认为oracle足够聪明,可以将效率较低的一个(无论哪个)转换为另一个。因此,我认为答案应该取决于每个人的可读性(我认为IN显然胜出)


2

OR当需要比较的值较少时(从可读性的角度来看)是有道理的。 IN特别有用。当您有一个动态源时,您希望与之进行比较。

另一种选择是将a JOIN与临时表一起使用。
只要您有必要的索引,我认为性能应该不是问题。


-2

我在大量OR(350)中进行了SQL查询。Postgres做了437.80ms

使用或

现在使用IN:

用于

23.18毫秒


4
那不是完全一样的东西,因为您已经为IN子句使用了子查询。
gliljas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.