1

出于好奇,我发现这个问题是因为 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 个参数,它就会阻塞。

我的问题:这是已知的,可接受的行为还是某种错误?如果这是可接受的行为,那么我必须尝试哪些选项来解决它?内联参数值是可行的,尽管它对我来说不是最佳解决方案(我必须破解 Entity Framework 以强制它不使用参数化查询)。

编辑——部分答案:正如@JoeW 建议的那样,我将查询的执行计划与 192 个参数和 193 个参数进行了比较,它们确实不同。对于 192 个参数,执行计划基本上是 64INDEX SEEK秒(每行一个),它们MERGE JOIN一起编辑。在 193 个参数处,执行计划切换到单个INDEX SCAN然后FILTERs 结果。很有意思。使用内联的所有值运行相同的查询会生成一个执行计划,该计划只执行INDEX SEEKonly、noJOINSCANs。

所以问题不是严格与参数的数量有关,而是与索引搜索的数量有关,但这只是参数化查询的问题。确实非常有趣。

4

2 回答 2

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
于 2013-07-10T16:48:21.097 回答
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 ...
于 2013-07-10T17:50:33.873 回答