MySQL / PostgreSQL中IN关键字的速度

时间:2009-06-05 18:36:46

标签: mysql performance postgresql list

我听过很多人说大多数关系数据库中的IN关键字都很慢。这是真的吗?一个示例查询就是这个,在我的脑海中:

SELECT * FROM someTable WHERE someColumn IN (value1, value2, value3)

我听说这比这样做慢得多:

SELECT * FROM someTable WHERE
  someColumn = value1 OR
  someColumn = value2 OR
  someColumn = value3

这是真的吗?或者速度差是否可以忽略不计?如果重要的话,我正在使用PostgreSQL,但我也想知道MySQL如何运行(如果它有任何不同)。提前谢谢。

7 个答案:

答案 0 :(得分:13)

在PostgreSQL中,你在这里得到的确切取决于底层表,所以你应该对一些有用的数据子集使用EXPLAIN ANALYZE进行一些示例查询,以确定优化器将要做什么(确保你正在运行的表格也已经过分析。 IN可以通过几种不同的方式处理,这就是为什么你需要查看一些样本来确定哪种替代方法用于你的数据。你的问题没有简单的通用答案。

至于您在修订版中添加的具体问题,针对这里没有涉及索引的简单数据集,您将获得两个查询计划的示例:

postgres=# explain analyze select * from x where s in ('123','456');
 Seq Scan on x  (cost=0.00..84994.69 rows=263271 width=181) (actual time=0.015..1819.702 rows=247823 loops=1)
   Filter: (s = ANY ('{123,456}'::bpchar[]))
 Total runtime: 1931.370 ms

postgres=# explain analyze select * from x where s='123' or s='456';
 Seq Scan on x  (cost=0.00..90163.62 rows=263271 width=181) (actual time=0.014..1835.944 rows=247823 loops=1)
   Filter: ((s = '123'::bpchar) OR (s = '456'::bpchar))
 Total runtime: 1949.478 ms

这两个运行时基本相同,因为实际处理时间由表中的顺序扫描主导;多次运行显示两者之间的差异低于运行运行误差范围。正如您所看到的,PostgreSQL将IN情况转换为使用其ANY过滤器,该过滤器应始终比一系列OR更快地执行。同样,这个微不足道的案例并不一定代表您在涉及索引等的严肃查询中会看到的内容。无论如何,用一系列OR语句手动替换IN应该永远不会更快,因为如果有优秀的数据可用,优化器就知道这里要做的最好的事情。

一般来说,PostgreSQL比MySQL优化器更了解如何优化复杂查询,但它也很大程度上依赖于你给优化器提供了足够的数据。 PostgreSQL wiki的“性能优化”部分的第一个链接涵盖了从优化器获得良好结果所需的最重要的事情。

答案 1 :(得分:8)

MySQL中,这些是优化程序的完整同义词:

SELECT  *
FROM    someTable
WHERE   someColumn IN (value1, value2, value3)

SELECT  *
FROM    someTable
WHERE   someColumn = value1 OR
        someColumn = value2 OR
        someColumn = value3

,前提是value是文字内容或预设变量。

根据documentation

  

单部分索引的范围条件的定义如下:

     
      
  • 对于BTREEHASH索引,使用=<=>,{{1}时,使用常量值对关键部分进行比较是一个范围条件},IN()IS NULL运营商。
  •   
  • ...
  •   
  • 对于所有类型的索引,多个范围条件与IS NOT NULLOR组合形成范围条件。
  •   
     

前面描述中的“常量值”表示以下之一:

     
      
  • 来自查询字符串的常量
  •   
  • 来自同一连接的const或系统表的列
  •   
  • 不相关子查询的结果
  •   
  • 任何完全由前面类型的子表达式组成的表达式
  •   

但是,这个查询:

AND

将使用SELECT * FROM table WHERE id = 1 OR id = (SELECT id FROM other_table WHERE unique_condition) 上的索引,而这一个:

id

将使用fullscan。

予。即当SELECT * FROM table WHERE id IN (1, (SELECT id FROM other_table WHERE unique_condition)) 之一是单行子查询时存在差异。

我最近在value中以bug 45145提交了该文件(结果是MySQL具体,5.2缺席,5.1更正了)

答案 2 :(得分:5)

使用IN不一定很慢,它是如何构建IN参数的,这会大大减慢速度。人们常常使用SELECT ... WHERE x IN(SELECT ...,它可能非常优化(即根本没有)。搜索“相关子查询”以查看它有多糟糕。

通常你根本不必使用IN,而是可以使用JOIN,并利用派生表。

SELECT * FROM table1 WHERE x IN (SELECT y FROM table2 WHERE z=3)

可以这样改写

SELECT * FROM table1 JOIN (SELECT y FROM table2 WHERE z=3) AS table2 ON table1.x=table2.y

如果IN语法很慢,JOIN语法通常会快得多。您可以使用EXPLAIN查看每个查询的优化方式。这是一个简单的示例,您的数据库可能会显示相同的查询路径,但更复杂的查询通常会显示不同的内容。

答案 3 :(得分:1)

带有子选择的IN通常很慢。带有值列表的IN不应该比someColumn = value1或someColumn = value2或someColumn = value3等慢。只要值的数量是合理的,这是非常快的。

当优化器无法找到执行查询的好方法时,

IN子查询很慢,并且必须使用显式方法来构建子查询的完整结果。例如:

SELECT username
  FROM users
  WHERE userid IN (
    SELECT userid FROM users WHERE user_first_name = 'Bob'
  )

会比

慢得多
SELECT username FROM users WHERE user_first_name = 'Bob'

除非优化器能够弄清楚你的意思。

答案 4 :(得分:1)

我认为你得到了你想要的答案。只想添加一件事。

您需要优化IN并以正确的方式使用它。在开发过程中,我总是在有查询的时候在页面底部设置一个调试部分,它会自动在每个SELECT上运行EXPLAIN EXTENDED,然后显示SHOW WARNINGS以查看MySQL的查询优化器将重写的(可能)方式内部查询。很多东西可以从中学到如何确保IN为你工作。

答案 5 :(得分:0)

它在文档中说IN在MySQL中非常快,但我目前找不到源。

答案 6 :(得分:0)

IN关键字的速度实际上取决于子查询的复杂性。在您提供的示例中,您只想查看someColumns值是否在设置的值列表中,并且是非常短的值。因此,我认为在这种情况下,性能成本将非常小。