【问题标题】:IN vs OR of Oracle, which faster?Oracle的IN vs OR,哪个更快?
【发布时间】:2011-09-24 18:53:48
【问题描述】:

我正在开发一个应用程序来处理 Oracle 数据库中的许多数据。
在某些情况下,我必须根据给定的条件列表获取许多对象,我使用SELECT ...FROM.. WHERE... IN...,但IN 表达式只接受一个最大为 1,000 个项目的列表。

所以我改用OR 表达式,但正如我观察到的——也许这个查询(使用OR)比IN 慢(使用相同的条件列表)。这样对吗?如果是这样,如何提高查询速度?

【问题讨论】:

  • 列表是静态的还是来自查询?
  • 否,要查询的值列表是从外部资源中检索的。有什么办法可以解决这个问题,因为我的列表太大,可能包含超过 100000 个项目
  • 那么,您正在创建一个包含类似 IN (...9997, 9998, 9999, 1000,1001 ...) 的大型查询字符串?传输和解析这本身就会花费很多。没关系 sql 注入的可能性。

标签: sql performance oracle


【解决方案1】:

INOR 更可取——OR 是出了名的糟糕表现,并且可能导致需要在复杂查询中使用括号的其他问题。

INOR 更好的选择是加入包含您想要(或不想要)的值的表。此用于比较的表可以是派生的、临时的或已存在于您的架构中。

【讨论】:

  • 不,我只查询一张表。我的列表可能包含太多项目,因此我不能使用 IN。我尝试将列表拆分成更小的部分,对一批子列表进行查询,但后来我必须在内存中排序数据,这很慢。
  • IN 和 OR 不一样吗?也就是说,无论如何 IN 扩展到 OR ?这就是 NOT IN with NULL 失败的原因
  • @gbn:从逻辑上讲,是的。但是IN 比使用OR 进行了优化——它不仅仅是语法糖。
  • 你能证明 IN 不仅仅是语法糖吗?我在答案中测试并发布了结果。
  • @jva:一般政策是有人回应后投反对票,而不是之前投反对票。坦率地说,这是试图宣传您的答案。请记住,答案会老化 - 较新的版本可能意味着无法保证行为。
【解决方案2】:

在这种情况下,我会这样做:

  1. 创建单列全局临时表
  2. 使用您从外部来源获得的列表填充此表(并且快速 - 另一个完整的讨论)
  3. 通过将临时表连接到另一个表来执行查询(考虑动态抽样,因为临时表不会有很好的统计信息)

这意味着您可以将排序留给数据库并编写一个简单的查询。

【讨论】:

    【解决方案3】:

    无论如何,Oracle 在内部将 IN 列表转换为 OR 列表,因此实际上应该没有性能差异。唯一的区别是 Oracle 必须转换 IN,但如果您自己提供 OR,则需要解析更长的字符串。

    这是你如何测试它。

    CREATE TABLE my_test (id NUMBER);
    
    SELECT 1 
    FROM my_test
    WHERE id IN (1,2,3,4,5,6,7,8,9,10,
                 21,22,23,24,25,26,27,28,29,30,
                 31,32,33,34,35,36,37,38,39,40,
                 41,42,43,44,45,46,47,48,49,50,
                 51,52,53,54,55,56,57,58,59,60,
                 61,62,63,64,65,66,67,68,69,70,
                 71,72,73,74,75,76,77,78,79,80,
                 81,82,83,84,85,86,87,88,89,90,
                 91,92,93,94,95,96,97,98,99,100
                 );
    
    SELECT sql_text, hash_value
    FROM v$sql 
    WHERE sql_text LIKE '%my_test%';
    
    SELECT operation, options, filter_predicates
    FROM v$sql_plan
    WHERE hash_value = '1181594990'; -- hash_value from previous query
    

    选择声明
    表访问已满(“ID”=1 或“ID”=2 或“ID”=3 或“ID”=4 或“ID”=5 或 "ID"=6 或 "ID"=7 或 "ID"=8 或 "ID"=9 或 "ID"=10 或 "ID"=21 或 "ID"=22 或 "ID"=23 或 "ID"=24 或 "ID"=25 或 "ID"=26 或 "ID"=27 或 "ID"=28 或 "ID"=29 或 "ID"=30 或 "ID"=31 或 "ID"=32 或 "ID"=33 或 "ID"=34 或 "ID"=35 或 "ID"=36 或 "ID"=37 或 "ID"=38 或 "ID"=39 或 "ID"=40 或 "ID"=41 或 "ID"=42 或 "ID"=43 或 "ID"=44 或 "ID"=45 或 "ID"=46 或 "ID"=47 或 "ID"=48 或 "ID"=49 或 "ID"=50 或 "ID"=51 或 "ID"=52 或 "ID"=53 或 "ID"=54 或 "ID"=55 或 "ID"=56 或 "ID"=57 或 "ID"=58 或 "ID"=59 或 "ID"=60 或 "ID"=61 或 "ID"=62 或 "ID"=63 或 "ID"=64 或 "ID"=65 或 "ID"=66 或 "ID"=67 或 "ID"=68 或 "ID"=69 或 "ID"=70 或 "ID"=71 或 "ID"=72 或 "ID"=73 或 "ID"=74 或 "ID"=75 或 "ID"=76 或 "ID"=77 或 "ID"=78 或 "ID"=79 或 "ID"=80 或 "ID"=81 或 "ID"=82 或 "ID"=83 或 "ID"=84 或 "ID"=85 或​​ "ID"=86 或 "ID"=87 或 "ID"=88 或 "ID"=89 或 "ID"=90 或 "ID"=91 或 "ID"=92 或 "ID"=93 或 "ID"=94 或 "ID"=95 或 "ID"=96 或 "ID"=97 或 "ID"=98 或 "ID"=99 或 "ID"=100)

    【讨论】:

    • 您创建的表是堆表 - 没有主键/索引
    • @OMGPonies Plus 1 关于您不反对 jva 作为报复的回答。我知道这里有很多人会这样做。
    【解决方案4】:

    我会质疑整个方法。 SP 的客户端必须发送 100000 个 ID。客户从哪里获得这些 ID?发送如此大量的 ID 作为 proc 的参数无论如何都会花费很大的成本。

    【讨论】:

      【解决方案5】:

      如果您使用主键创建表:

      CREATE TABLE my_test (id NUMBER,
      CONSTRAINT PK PRIMARY KEY (id));
      

      并通过相同的 SELECT 运行具有多个 IN 值的查询,然后通过哈希值检索执行计划,您得到的是:

      SELECT STATEMENT
      INLIST ITERATOR
      INDEX                  RANGE SCAN
      

      这似乎暗示,当您有一个 IN 列表并将其与 PK 列一起使用时,Oracle 将在内部将该列表保留为“INLIST”,因为它更有效地处理它,而不是将其转换为 OR,如未索引表的情况。

      我使用的是上面的 Oracle 10gR2。

      【讨论】:

      • 感谢测试。如果该列没有被索引怎么办?还会是多重OR吗?
      猜你喜欢
      • 2010-11-10
      • 2019-07-15
      • 2012-01-03
      • 2020-08-04
      • 1970-01-01
      • 2011-06-12
      • 1970-01-01
      • 1970-01-01
      • 2016-06-04
      相关资源
      最近更新 更多