SQL WHERE ID IN(id1,id2,…,idn)


170

我需要编写查询来检索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
选项1显着减少了SQL Server响应时间,选择了7k ID(其中一些不存在)。通常,查询花费了1300毫秒,使用IN!减少为80毫秒。我的解决方案是1 +3。最后一个查询是一个长查询字符串,发送给SQL以执行。
Piotr Kula 2015年

Answers:


108

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

为什么?

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

  • 选项3简直就是可怕的性能。它在每个循环中发送一个查询,并通过少量查询来锤击数据库。它还阻止它对“值是给定列表中的值之一”使用任何优化。


2
我同意,但是请注意,在许多RDMS中,in列表是受限制的,因此您需要我们使用@Ed Guiness的解决方案,但此处的临时表在RDBMS之间确实有所不同。(有效地解决了复杂的问题,您不能只使用纯标准的SQL)
mmmmmm

28

一种替代方法可能是使用另一个表包含id值。然后,可以将该另一个表内部连接到TABLE上,以约束返回的行。这将具有主要优势,您将不需要动态SQL(在最佳情况下是有问题的),并且您将不会有无限长的IN子句。

您将截断另一个表,插入大量行,然后创建索引以提高联接性能。它还可以让您从数据检索中分离出这些行的累积,也许为您提供了更多性能调整选项。

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


1
但是,您将如何传递所需的ID列表?(看到您无法选择范围或类似内容)。
raam86

1
@ raam86:ID列表可能已使用select另一个表上的语句获得。该列表将作为您要inner join针对的另一个表传递。
bdforbes

19

埃德·吉尼斯(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#')

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

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

并且性能大大提高。


1
嗨,fnSplitter是MSSQL的函数吗?因为我找不到它。
WiiMaxx 2015年

这不是标准的事情。他们必须表示为此目的编写了该功能,或者例如已经有一个提供此功能的应用程序。
underscore_d

fnSplitter是Ritu创建的一个函数,您可以在Internet / Google上找到类似的函数
Bashar Abu Shamaa,

9

第一选择肯定是最好的选择。

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

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

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

为什么要分成大块?

您将永远不会遇到内存溢出异常,这在像您这样的场景中非常普遍。您将优化数据库调用次数,从而提高性能。

对于我来说,它一直都是魅力。希望它也适用于我的其他开发人员:)


4

对具有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

使用联接。


3

在大多数数据库系统中,IN (val1, val2, …)一系列OR都针对同一计划进行了优化。

第三种方法是将值列表导入到临时表中并进行连接,如果有很多值,则在大多数系统中效率更高。

您可能需要阅读以下文章:


3

在所有样本中,样本3都是性能最差的,这是因为您无缘无故地尝试访问数据库很多次。

到目前为止,将数据加载到临时表中然后再进行联接是最快的。之后,IN应该比OR组的工作速度稍快。


2

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


1
大约40k个IN元素后,甚至SQL Server也停止工作。根据MSDN:在IN子句中包含大量值(数千个)会消耗资源并返回错误8623或8632。要变通解决此问题,请将IN列表中的项目存储在表中。
jahav
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.