【问题标题】:SQL WHERE ID IN (id1, id2, ..., idn)SQL WHERE ID IN (id1, id2, ..., idn)
【发布时间】:2011-08-13 19:01:55
【问题描述】:

我需要编写一个查询来检索一个大的 id 列表。

我们确实支持许多后端(MySQL、Firebird、SQLServer、Oracle、PostgreSQL ...),所以我需要编写一个标准 SQL。

id 集的大小可能很大,查询将以编程方式生成。那么,最好的方法是什么?

1) 使用 IN 编写查询

SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)

我的问题是。如果 n 很大会怎样?另外,性能怎么样?

2) 使用 OR 编写查询

SELECT * FROM TABLE WHERE ID = id1 OR ID = id2 OR ... OR ID = idn

我认为这种方法没有n的限制,但是如果n很大的话性能呢?

3) 编写程序化解决方案:

  foreach (var id in myIdList)
  {
      var item = GetItemByQuery("SELECT * FROM TABLE WHERE ID = " + id);
      myObjectList.Add(item);
  }

当通过网络查询数据库服务器时,我们遇到了这种方法的一些问题。通常,做一个检索所有结果的查询比做很多小查询要好。也许我错了。

这个问题的正确解决方案是什么?

【问题讨论】:

  • 选项 1 显着减少了 SQL 服务器响应时间,选择了 7k 个 ID,其中一些不存在。通常查询大约需要 1300 毫秒,使用 IN 可以减少到 80 毫秒!我将我的作为您的解决方案 1 + 3。只是最后一个查询是一个发送到 SQL 执行的长查询字符串。

标签: sql select


【解决方案1】:

选项 1 是唯一好的解决方案。

为什么?

  • 选项 2 的作用相同,但您多次重复列名;此外,SQL 引擎不会立即知道您要检查该值是否是固定列表中的值之一。但是,一个好的 SQL 引擎可以对其进行优化,使其具有与 IN 相同的性能。但是仍然存在可读性问题...

  • 选项 3 在性能方面简直太糟糕了。它在每个循环中发送一个查询,并用小查询锤击数据库。它还阻止它对“值是给定列表中的值之一”使用任何优化

【讨论】:

  • 我同意,但请注意,in 列表在许多 RDMS 中是有限的,因此您需要我们使用@Ed Guness 的解决方案,但这里的临时表在 RDBMS 之间确实不同。 (对于不能只使用纯标准 SQL 的复杂问题非常有效)
  • @ThiefMaster 如果表包含 n 行并且列表包含 m 项,如果 ID 列上没有索引,复杂度是 n*m 吗?
【解决方案2】:

另一种方法可能是使用另一个表来包含 id 值。然后可以在您的 TABLE 上内连接该其他表以限制返回的行。这将有一个主要优点,即您不需要动态 SQL(在最好的情况下会出现问题),并且您不会有无限长的 IN 子句。

您将截断这个其他表,插入大量行,然后可能创建一个索引来提高连接性能。它还可以让您将这些行的累积与数据检索分离,或许可以为您提供更多选项来调整性能。

更新:尽管您可以使用临时表,但我并不是要暗示您必须甚至应该这样做。用于临时数据的永久表是一种常见的解决方案,其优点超出了此处所述。

【讨论】:

  • 但是你将如何传递你需要的 id 列表呢? (看到你不能选择一个范围或类似的东西)。
  • @raam86:ID 列表可能是在另一个表上使用select 语句获得的。该列表作为您 inner joining 反对的另一个表传递。
【解决方案3】:

Ed Guiness 建议的确实是性能提升器,我有一个这样的查询

select * from table where id in (id1,id2.........long list)

我做了什么:

DECLARE @temp table(
            ID  int
            )
insert into @temp 
select * from dbo.fnSplitter('#idlist#')

然后inner将temp与主表连接起来:

select * from table inner join temp on temp.id = table.id

而且性能大幅提升。

【讨论】:

  • 嗨,fnSplitter 是 MSSQL 的一个函数吗?因为我没找到。
  • 这不是标准的东西。他们必须表示他们为此目的编写了该函数,或者例如有一个应用程序已经提供了它。
  • fnSplitter 是 Ritu 创建的一个函数,你可以在 internet/google 上找到类似的函数
【解决方案4】:

第一个选项绝对是最好的选择。

SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)

但是考虑到 id 列表非常庞大,比如说数百万,您应该考虑如下块大小:

  • 将您的 ID 列表划分为固定数字的块,例如 100
  • 应根据服务器的内存大小确定块大小
  • 假设您有 10000 个 Id,您将有 10000/100 = 100 个块
  • 一次处理一个块,导致 100 次数据库调用选择

为什么要分块?

您永远不会遇到内存溢出异常,这在您这样的场景中很常见。 您将优化数据库调用次数,从而获得更好的性能。

它对我来说一直很有吸引力。希望它也适用于我的开发人员:)

【讨论】:

    【解决方案5】:

    对包含 5 亿条记录的 Azure SQL 表执行 SELECT * FROM MyTable where id in () 命令导致等待时间超过 7 分钟!

    这样做会立即返回结果:

    select b.id, a.* from MyTable a
    join (values (250000), (2500001), (2600000)) as b(id)
    ON a.id = b.id
    

    使用连接。

    【讨论】:

      【解决方案6】:

      在大多数数据库系统中,IN (val1, val2, …) 和一系列OR 被优化为同一个计划。

      第三种方法是将值列表导入一个临时表并加入它,如果有很多值,这在大多数系统中效率更高。

      您可能想阅读这篇文章:

      【讨论】:

        【解决方案7】:

        我认为您的意思是 SqlServer,但在 Oracle 上,您有一个硬性限制,您可以指定多少 IN 元素:1000。

        【讨论】:

        • 甚至 SQL Server 在大约 40k IN 元素后停止工作。根据 MSDN:在 IN 子句中包含大量值(数千个)会消耗资源并返回错误 8623 或 8632。要解决此问题,请将 IN 列表中的项目存储在表中。
        【解决方案8】:

        示例 3 将是所有这些中表现最差的,因为您无缘无故地无数次访问数据库。

        将数据加载到临时表中然后加入该表将是迄今为止最快的。之后,IN 的工作速度应该比 OR 组稍快。

        【讨论】:

          【解决方案9】:
          1. 对于第一个选项
            将 ID 添加到临时表并添加与主表的内连接。
          CREATE TABLE #temp (column int)
          INSERT INTO #temp (column) 
          SELECT t.column1 FROM (VALUES (1),(2),(3),...(10000)) AS t(column1)
          

          【讨论】:

            【解决方案10】:

            试试这个

            SELECT Position_ID , Position_Name
            FROM 
            position
            WHERE Position_ID IN (6 ,7 ,8)
            ORDER BY Position_Name
            

            【讨论】:

              猜你喜欢
              • 2019-06-09
              • 2015-07-12
              • 2019-06-19
              • 1970-01-01
              • 2020-06-15
              • 1970-01-01
              • 1970-01-01
              • 2019-09-19
              相关资源
              最近更新 更多