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

当通过网络查询数据库服务器时,我们使用这种方法遇到了一些问题。通常情况下,做一个查询来检索所有结果比做很多小查询更好。也许我错了。

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


当前回答

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

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

更新:尽管您可以使用临时表,但我并不是说您必须或应该使用临时表。用于临时数据的永久表是一种常见的解决方案,其优点超出了本文所述。

其他回答

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

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

更新:尽管您可以使用临时表,但我并不是说您必须或应该使用临时表。用于临时数据的永久表是一种常见的解决方案,其优点超出了本文所述。

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

Why?

Option 2 does the same but you repeat the column name lots of times; additionally the SQL engine doesn't immediately know that you want to check if the value is one of the values in a fixed list. However, a good SQL engine could optimize it to have equal performance like with IN. There's still the readability issue though... Option 3 is simply horrible performance-wise. It sends a query every loop and hammers the database with small queries. It also prevents it from using any optimizations for "value is one of those in a given list"

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

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

然而,考虑到id的列表非常庞大,比如数百万,你应该考虑如下所示的块大小:

将id列表划分为固定数量的块,比如100 块大小应该根据服务器的内存大小来决定 假设你有10000个id,你将有10000/100 = 100个块 每次处理一个数据块,导致100次数据库select调用

为什么要分成几部分?

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

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

第一种选择 在临时表中添加id,并添加与主表的内部连接。

CREATE TABLE #temp (column int)
INSERT INTO #temp (column) 
SELECT t.column1 FROM (VALUES (1),(2),(3),...(10000)) AS t(column1)

在大多数数据库系统中,In (val1, val2,…)和一系列OR被优化为同一计划。

第三种方法是将值列表导入到一个临时表中并连接它,如果有很多值,这种方法在大多数系统中更有效。

你可能会想读这篇文章:

在MySQL中传递参数:in列表vs.临时表