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