我需要编写一个查询来检索一大堆ID。
我们支持许多后端(MySQL,Firebird,SQLServer,Oracle,PostgreSQL ......)所以我需要编写一个标准的SQL。
id集的大小可能很大,查询将以编程方式生成。那么,最好的方法是什么?
SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)
我的问题是。如果n很大会怎么样?还有,性能呢?
SELECT * FROM TABLE WHERE ID = id1 OR ID = id2 OR ... OR ID = idn
我认为这种方法没有n限制,但如果n非常大,那么性能呢?
foreach (id in myIdList)
{
item = GetItemByQuery("SELECT * FROM TABLE WHERE ID = " + id);
myObjectList.Add(item);
}
当通过网络查询数据库服务器时,我们遇到了这种方法的一些问题。通常最好做一个检索所有结果的查询,更好的是那么多小的查询。也许我错了。
这个问题的正确解决方案是什么?
答案 0 :(得分:86)
选项1是唯一的好解决方案。
选项2执行相同操作但您重复列名称很多次;此外,SQL引擎不会立即知道您要检查该值是否是固定列表中的值之一。但是,一个好的SQL引擎可以优化它以获得与IN
相同的性能。尽管如此......仍然存在可读性问题。
选项3的表现非常糟糕。它在每个循环中发送一个查询,并使用小查询对数据库进行锤击。它还阻止它使用“值是给定列表中的值之一”的任何优化
答案 1 :(得分:23)
另一种方法可能是使用另一个表来包含id值。然后,可以将另一个表内部连接到TABLE上以约束返回的行。这将具有以下主要优点:您不需要动态SQL(在最好的时候会出现问题),并且您将不会拥有无限长的IN子句。
您将截断此其他表,插入大量行,然后创建索引以帮助加入性能。它还可以让您从数据检索中分离这些行的累积,或许可以为您提供更多选项来调整性能。
更新:虽然您可以使用临时表,但我并不是说您必须或甚至应该这样做。用于临时数据的永久表是一种常见的解决方案,其优点超出此处所述。
答案 2 :(得分:10)
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
性能大幅提升。
答案 3 :(得分:7)
第一个选项绝对是最佳选择。
SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)
然而考虑到id的列表非常庞大,比如数百万,你应该考虑下面的块大小:
为什么要分成几块?
你永远不会得到内存溢出异常,这在像你这样的场景中很常见。 您将拥有优化的数据库调用次数,从而提高性能。
对我而言,它总是像魅力一样。希望它对我的同事们也有用:)
答案 4 :(得分:3)
在大多数数据库系统中,IN (val1, val2, …)
和一系列OR
针对同一计划进行了优化。
第三种方法是将值列表导入临时表并加入,如果有大量值,则在大多数系统中更有效。
您可能想阅读这篇文章:
答案 5 :(得分:3)
样本3将是表现最差的,因为你没有明显的理由无数次地使用数据库。
将数据加载到临时表中,然后加入其中,这是迄今为止最快的。之后,IN应该比OR组稍快一些。
答案 6 :(得分:3)
在具有5亿条记录的Azure SQL表上执行SELECT * FROM MyTable,其中id in()命令导致等待时间> 1。 7分钟!
执行此操作会立即返回结果:
select b.id, a.* from MyTable a
join (values (250000), (2500001), (2600000)) as b(id)
ON a.id = b.id
使用联接。
答案 7 :(得分:2)
我认为你的意思是SqlServer但是在Oracle上你有一个硬限制你可以指定多少个IN元素:1000。
答案 8 :(得分:0)
尝试一下
SELECT Position_ID , Position_Name
FROM
position
WHERE Position_ID IN (6 ,7 ,8)
ORDER BY Position_Name