许多值的 SQL Server IN 子句的等效速度更快

Faster equivalent of SQL Server IN clause for many values

我在 SQL Server 12.0 中使用 OrmLite .NET。我想要 select 个实体,其中某个整数列(不是主键)具有许多值之一,我在数组中有这些值。像这样的 OrmLite 表达式:

q => query.Where(r => myIntegers.Contains(r.TheColumn))

翻译成

WHERE "TheColumn" IN (1, 2, 3, ...) -- my integers

这在 ~100 时工作正常,但在 1000 时超时。如何使用更大的列表实现相同的效果?我可以通过某种方式将数组或 table 参数传递给 SQL 服务器吗?

与@JoeTaras 评论的类似,您可以将可接受的值放入子查询中,例如;

SELECT TheColumn from TheTable T
INNER JOIN (SELECT * from (VALUES(1),(2),(3),(4)) as V1(value)) V
ON T.TheColumn = V.value

在 OrmLite 中,您可以构建您需要的原始 SQL 查询(例如,来自 @Sam cd answer)并将其传递给 db.Select 方法:

string joinedIds = string.Join("),(", new List<int> {1, 2, 9});

string command =
    $"SELECT * from Employee AS E INNER JOIN (SELECT * from (VALUES( {joinedIds} )) as TV(value)) V ON E.Id = V.value";

List<Employee> employers = db.Select<Employee>(command);

我最终为此使用了临时 table,即:

CREATE TABLE #ParamTempTable (Id BIGINT NOT NULL PRIMARY KEY);

INSERT INTO #ParamTempTable (Id) VALUES ((1), (2), (3));

SELECT *
FROM MyTable
WHERE Id IN (SELECT * FROM #ParamTempTable);

DROP TABLE #ParamTempTable;

只有当我有 > 1000 个值时我才这样做,否则我只使用 SQL IN (1, 2, 3).

为了在 OrmLite 上实现这个,我必须 subclass SqlServerExpression<T> 并覆盖 VisitStaticArrayMethodCallVisitEnumerableMethodCall 来改变它处理 Contains 表达式的方式。此 class 存储用于分别创建和删除临时 table 的命令,我必须 运行 为每个命令分隔一个 SqlCommand。 (不幸的是,CREATE TABLESqlCommand.ExecuteReader 中不起作用。)所以这最终变得相当复杂,但它确实有效并且对调用者是透明的。一个额外的好处是我可以消除重复的参数,例如对于 o => ids.Contains(o.FromId) || ids.Contains(o.ToId),列表 ids 仅发送到 SQL 服务器一次。