对于好奇,我发现这个问题是因为Entity Framework生成的查询,但是我通过SSMS运行普通的SQL命令来重现它。
摘要:我正在运行一个简单查询,通过查询复合主键来检查现有数据。如果我运行具有193个或更多参数的查询的参数化版本,则查询永远不会完成(我在取消之前等待了5分钟)。如果我使用192或更少的参数运行类似的查询,它几乎立即完成,正如主键查找所期望的那样。
后台:我的下表带有群集复合主键:
CREATE TABLE MyTable {
KeyCol1 INT NOT NULL,
KeyCol2 INT NOT NULL,
KeyCol3 INT NOT NULL,
OtherCol1 INT NOT NULL,
OtherCol2 INT NOT NULL,
...
CONSTRAINT PK_MyTable PRIMARY KEY CLUSTERED (KeyCol1 ASC, KeyCol2 ASC, KeyCol3 ASC)
}
此表目前有大约5200万行。如果我想检查一个特定行的存在,我可能会这样做:
SELECT 1 FROM MyTable WHERE KeyCol1 = 100 AND KeyCol2 = 200 AND KeyCol3 = 300
无论是否找到匹配的行,都会立即返回。如果我想检查是否存在多个不同的行,并返回找到哪些行,我将需要OR
以及几个条件:
SELECT KeyCol1, KeyCol2, KeyCol3 FROM MyTable
WHERE (KeyCol1 = 100 AND KeyCol2 = 200 AND KeyCol3 = 300)
OR (KeyCol1 = 101 AND KeyCol2 = 201 AND KeyCol3 = 301)
OR ...
即使我有超过300个不同的行,我也会立即返回。
问题:如果我采用完全相同的查询但将值拉出到参数中,则查询永远不会完成:
DECLARE @val1 INT = 100;
DECLARE @val2 INT = 200;
DECLARE @val3 INT = 300;
DECLARE @val4 INT = 101;
DECLARE @val5 INT = 201;
DECLARE @val6 INT = 301;
...
SELECT KeyCol1, KeyCol2, KeyCol3 FROM MyTable
WHERE (KeyCol1 = @val1 AND KeyCol2 = @val2 AND KeyCol3 = @val3)
OR (KeyCol1 = @val4 AND KeyCol2 = @val5 AND KeyCol3 = @val6)
OR ...
我开始将参数数量一分为二,直到我发现192似乎是一个神奇的数字。使用上面最多192个参数的查询工作正常,它返回的速度几乎与内联硬编码值一样快(几毫秒的性能损失很小)。但是,只要我向查询添加第193个参数,它就会窒息。
我的问题:这是已知的,被接受的行为还是某种错误?如果它被接受了,那么我有什么选择可以尝试解决它?内联参数值有效,虽然它对我来说是次优解决方案(我必须破解实体框架以强制它不使用参数化查询)。
编辑 - 部分答案:正如@JoeW建议的那样,我将查询的执行计划与192个参数和193个参数进行了比较,它们确实不同。对于192个参数,the execution plan基本上是64 INDEX SEEK
s(每行一个)MERGE JOIN
。在193个参数中,the execution plan切换为单个INDEX SCAN
,然后FILTER
结果。很有意思。使用内联的所有值运行相同的查询会生成an execution plan,只会INDEX SEEK
,不会JOIN
或SCAN
。
因此,问题与参数的数量并不严格相关,而是与索引搜索的数量有关,但这只是参数化查询的问题。非常有趣。
答案 0 :(得分:1)
尝试为3列为val1, val2, val3
的所有变量创建临时表,并加入这两个表,您将获得答案。
CREATE TABLE TempTable {
ColumnId INT NOT NULL IDENTITY(1,1),
Col1 INT NOT NULL,
Col2 INT NOT NULL,
Col3 INT NOT NULL,
CONSTRAINT PK_TempTable PRIMARY KEY CLUSTERED (ColumnId)
}
将所有变量数据插入表格。
INSERT INTO TempTable (Col1, Col2, Col3)
VALUES (@val1, @val2, @val3), (@val4, @val5, @val6), ...
运行此查询:
SELECT t1.KeyCol1, t1.KeyCol2, t1.KeyCol3
FROM MyTable t1
INNER JOIN TempTable t2 ON t1.KeyCol1 = t2.Col1 AND t1.KeyCol2 = t2.Col2 AND t1.KeyCol3 = t2.Col3
答案 1 :(得分:0)
你让它运行多久了?我遇到过查询计划过于复杂而无法生成的问题。让我查看是否在让它运行一段时间后得到相同的错误。解决方法是将查询分解为较小的批次
SELECT KeyCol1, KeyCol2, KeyCol3 FROM MyTable
WHERE (KeyCol1 = @val1 AND KeyCol2 = @val2 AND KeyCol3 = @val3)
UNION ALL
SELECT KeyCol1, KeyCol2, KeyCol3 FROM MyTable
WHERE (KeyCol1 = @val4 AND KeyCol2 = @val5 AND KeyCol3 = @val6)
UNION ALL ...