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

问题描述 投票:0回答:11

我需要编写一个查询来检索大量 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);
  }

当通过网络查询数据库服务器时,我们遇到了这种方法的一些问题。通常,执行一次检索所有结果的查询比执行大量小查询更好。也许我错了。

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

sql select
11个回答
167
投票

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

为什么?

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

    IN
    相同的性能。但仍然存在可读性问题......

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


42
投票

Ed Guness 的建议确实是性能增强器,我有这样的查询

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

我做了什么:

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

然后将临时表与主表内部连接:

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

性能显着提高。


35
投票

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

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

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


21
投票

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

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

但是考虑到 ids 列表非常庞大,比如说数百万,你应该考虑像下面这样的块大小:

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

为什么要分成块?

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

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


10
投票

对包含 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
投票

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


3
投票

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

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


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

0
投票

这是我根据@Ed Guness'的建议对这个问题的看法。它比 @Ritu 的(无论如何它的语法都是无效的)好。

注意:您需要 SQL Server 2016 或更高版本 (COMPATIBILITY_MODE >= 130) 才能使用

STRING_SPLIT()
(或者您可以推出自己的等效函数)。

DECLARE @invoices AS VARCHAR(MAX)
SET @invoices = '4434845,4433863' -- long list of values

IF OBJECT_ID( 'tempdb..#tmp_invoices' ) IS NOT NULL 
BEGIN
    DROP TABLE #tmp_invoices 
END

SELECT value INTO #tmp_invoices FROM STRING_SPLIT( @invoices, ',' )

SELECT
    tlo.InvoiceId
    -- other fields here
FROM
    CRM.TrackedLabOrders AS tlo
    INNER JOIN #tmp_invoices on #tmp_invoices.value = tlo.InvoiceId

-1
投票

试试这个

SELECT Position_ID , Position_Name
FROM 
position
WHERE Position_ID IN (6 ,7 ,8)
ORDER BY Position_Name
© www.soinside.com 2019 - 2024. All rights reserved.