我需要编写一个查询来检索大量 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 (var id in myIdList)
{
var item = GetItemByQuery("SELECT * FROM TABLE WHERE ID = " + id);
myObjectList.Add(item);
}
当通过网络查询数据库服务器时,我们遇到了这种方法的一些问题。通常,执行一次检索所有结果的查询比执行大量小查询更好。也许我错了。
这个问题的正确解决方案是什么?
选项 1 是唯一好的解决方案。
选项 2 的作用相同,但您多次重复列名称;此外,SQL 引擎不会立即知道您要检查该值是否是固定列表中的值之一。然而,一个好的 SQL 引擎可以对其进行优化,使其具有与
IN
相同的性能。但仍然存在可读性问题......选项 3 的性能简直太糟糕了。它在每个循环中发送一个查询,并用小查询来敲击数据库。它还阻止它对“值是给定列表中的值之一”使用任何优化
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
性能显着提高。
另一种方法可能是使用另一个表来包含 id 值。然后可以将另一个表内部连接到您的 TABLE 上以限制返回的行。这样做的主要优点是您不需要动态 SQL(在最好的情况下也会出现问题),并且您不会有无限长的 IN 子句。
您可以截断另一个表,插入大量行,然后也许创建一个索引来帮助连接性能。它还可以让您将这些行的累积与数据检索分离,也许为您提供更多调整性能的选项。
更新:虽然您可以使用临时表,但我并不是暗示您必须甚至应该使用临时表。用于临时数据的永久表是一种常见的解决方案,其优点超出了此处描述的范围。
第一个选择绝对是最好的选择。
SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)
但是考虑到 ids 列表非常庞大,比如说数百万,你应该考虑像下面这样的块大小:
为什么要分成块?
您永远不会遇到内存溢出异常,这在像您这样的场景中很常见。 您将优化数据库调用数量,从而获得更好的性能。
它对我来说一直很有魅力。希望它也适用于我的开发人员同事:)
对包含 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
使用联接。
在大多数数据库系统中,
IN (val1, val2, …)
和一系列OR
都针对同一个计划进行优化。
第三种方法是将值列表导入到临时表中并连接它,如果有很多值,这在大多数系统中会更有效。
您可能想阅读这篇文章:
我认为你的意思是 SqlServer,但在 Oracle 上你有一个硬性限制,可以指定多少个 IN 元素:1000。
示例 3 将是其中表现最差的,因为您无缘无故地无数次访问数据库。
将数据加载到临时表中,然后加入该表是迄今为止最快的。之后,IN 的工作速度应该比 OR 组稍快一些。
CREATE TABLE #temp (column int)
INSERT INTO #temp (column)
SELECT t.column1 FROM (VALUES (1),(2),(3),...(10000)) AS t(column1)
这是我根据@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
试试这个
SELECT Position_ID , Position_Name
FROM
position
WHERE Position_ID IN (6 ,7 ,8)
ORDER BY Position_Name