【问题标题】:Oracle SQL: How to use more than 1000 items inside an IN clause [duplicate]Oracle SQL:如何在 IN 子句中使用超过 1000 个项目 [重复]
【发布时间】:2011-01-24 22:57:24
【问题描述】:

我有一个 SQL 语句,我想通过使用 IN 子句来获取 1200 ep_codes 的数据。当我在 IN 子句中包含超过 1000 个 ep_codes 时,Oracle 说我不允许这样做。为了克服这个问题,我尝试将 SQL 代码更改如下:

SELECT period, ...
FROM   my_view
WHERE  period = '200912'
       ...
       AND ep_codes IN (...1000 ep_codes...)
       OR  ep_codes IN (...200 ep_codes...)

代码已成功执行,但结果很奇怪(获取所有时段的计算结果,而不仅仅是 200912,这不是我想要的)。在IN 子句之间使用OR 这样做是否合适,或者我应该执行两个单独的代码,一个是1000,另一个是200 ep_codes?


Pascal Martin 的解决方案非常有效。感谢所有提供宝贵建议的人。

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    不确定在IN() 中使用这么多值是否真的很好——尤其是对于性能而言。

    当你说“结果很奇怪”时,可能是因为括号的问题?如果你试试这个,而不是你建议的:

    SELECT ...
    FROM ...
    WHERE ...
          AND (
              ep_codes IN (...1000 ep_codes...)
              OR  ep_codes IN (...200 ep_codes...)
          )
    

    它会让结果变得不那么奇怪吗?

    【讨论】:

    • 我已经更新了问题,以明确奇怪的结果。现在我正在尝试您提出的代码(带有额外的括号)。等结果出来了,我就告诉你。
    • 谢谢,现在可以正常使用了。我再次看到额外的括号是多么重要。
    • 不客气 :-) -- 是的,括号会很有帮助 ;-)
    【解决方案2】:

    在 Oracle 中处理此问题的推荐方法是创建一个临时表,将值写入此表,然后加入此表。使用动态创建的IN 子句意味着查询优化器会对每个查询进行“硬解析”。

    create global temporary table LOOKUP
    (
        ID NUMBER
    ) on commit delete rows;
    
    -- Do a batch insert from your application to populate this table
    insert into lookup(id) values (?)
    
    -- join to it
    select foo from bar where code in (select id from lookup)
    

    【讨论】:

    • 我们最近将一些代码从使用“in”更改为使用临时查找表。对于某些查询,性能从几分钟提高到一秒内。
    • 但这对服务器的多个并行请求有效吗?据我了解,Oracle 服务器上可能有几个打开的事务,试图创建 LOOKUP
    • @Ilya:见stackoverflow.com/questions/8240810/…。使用“on commit delete rows”,数据应该只在事务范围内可见。
    • @retronym:有趣的答案,我已经在我们的项目中尝试过了。但是我花了很多时间。可能我在插入数据时做错了。也就是说,我有大约 2500 个值应该插入到临时表中。您能否建议如何将这些值正确插入到 oracle DB 中?
    【解决方案3】:

    对于性能和可维护性而言,将代码放在单独的表中似乎是一个更好的主意。

    SELECT ...
    FROM ...
    WHERE ...
       AND ep_code in (select code from ep_code_table)
    

    【讨论】:

      【解决方案4】:

      您能否将 1200 个 ep_code 值插入临时表,然后将 INNER JOIN 插入该表以过滤行?

      SELECT a.*
      FROM mytable a
      INNER JOIN tmp ON (tmp.ep_code = a.ep_code)
      WHERE ...
      

      【讨论】:

      • 我不确定。我正在使用 IT 为我提供的一些视图。
      【解决方案5】:

      其实你可以在这里使用集合/多集合。您需要一个数字表类型来存储它们。

      CREATE TYPE NUMBER_TABLE AS TABLE OF NUMBER;
      ...
      SELECT *
      FROM my_view
      WHERE period MEMBER OF NUMBER_TABLE(1,2,3...10000)
      

      阅读更多关于多集的信息here

      【讨论】:

      • 这在语法上非常优雅,但性能令人失望。使用此语法选择单个主键值,CBO 会选择 INDEX FAST FULL SCAN。
      • 使用几千个 id 和一个中等大小的表(40K 行)进行测试,它仍然比在临时表上连接快大约 3 倍(由于哈希连接)。这还不包括创建和填充临时表的时间。
      猜你喜欢
      • 2014-10-30
      • 2017-02-13
      • 2010-09-28
      • 2016-11-22
      • 2018-11-20
      • 2013-03-30
      • 1970-01-01
      • 2011-06-10
      相关资源
      最近更新 更多