SQL WHERE ID IN(id1,id2,…,idn)
我需要写一个查询来检索一个大的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)编写一个编程解决scheme:
foreach (id in myIdList) { item = GetItemByQuery("SELECT * FROM TABLE WHERE ID = " + id); myObjectList.Add(item); }
当通过networking查询数据库服务器时,我们遇到了这种方法的一些问题。 通常情况下,最好做一个查询检索所有的结果,更好的是很多小的查询。 也许我错了。
什么是这个问题的正确解决scheme?
选项1是唯一的好的解决scheme。
为什么?
-
选项2也是一样的,但你重复列名很多次; 此外,SQL引擎不会立即知道您要检查值是否是固定列表中的值之一。 但是,一个好的SQL引擎可以优化它与
IN
相同的性能。 仍然有可读性问题,但… -
选项3在性能方面非常糟糕。 它每循环发送一个查询,并用小的查询敲击数据库。 它也阻止它使用任何优化“价值是给定列表中的一个”
另一种方法可能是使用另一个表来包含id值。 这个其他表然后可以在您的TABLE内部join,以约束返回的行。 这将有一个主要的优点,就是你不需要dynamicSQL(在最好的时候有问题),而且你不会有一个无限长的IN子句。
你会截断这个其他表,插入大量的行,然后可能创build一个索引来帮助连接性能。 它也可以让你从数据检索中分离出这些行的积累,也许给你更多的select来调整性能。
更新 :虽然你可以使用一个临时表,但我不是故意暗示你必须甚至应该。 用于临时数据的永久表是常见的解决scheme,具有超出此处描述的优点。
Guinessbuild议的东西真的是一个性能的助推器,我有这样的查询
select * from table where id in (id1,id2.........long list)
我做了什么 :
DECLARE @temp table( ID int ) insert into @temp select * from dbo.fnSplitter('#idlist#')
然后内部join主表的温度:
select * from table inner join temp on temp.id = table.id
性能大幅提升。
在大多数数据库系统中, IN (val1, val2, …)
和一系列OR
被优化为相同的计划。
第三种方法是将值列表导入到一个临时表中,并join到大多数系统中,如果有很多值的话。
你可能想阅读这篇文章:
- 在MySQL中传递参数:IN列表与临时表
样本3将是他们之中performance最差的一个,因为你没有明显的原因无数次地敲击数据库。
将数据加载到临时表中,然后join,将是迄今为止最快的。 之后,IN应该比OR组略快。
第一个选项绝对是最好的select。
SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)
但是考虑到ID列表非常庞大 ,比如说数百万,你应该考虑如下的块大小:
- 把你的ID列表分成固定数字的块,比如100
- 块大小应该根据您的服务器的内存大小来决定
- 假设你有10000个ID,你将有10000/100 = 100个块
- 一次处理一个块,导致100个数据库调用select
你为什么要分成几块?
你将永远不会得到内存溢出exception,这在你的场景中是非常普遍的。 您将优化数据库调用的数量,从而获得更好的性能。
它一直为我的魅力。 希望它可以为我的开发人员以及:)
我认为你的意思是SqlServer,但在Oracle上,你有一个硬限制你可以指定多lessIN元素:1000。