我的猜测是参数嗅探 - 当@includeAll
为1时编译的过程,这是已被高速缓存的查询计划。这意味着当它是假的时候,当潜在的和索引查找和密钥查找会更快时,你仍然在进行全表扫描。
我想这样做的最好的办法是:
declare @includeAll bit = 0;
if @includeAll = 1
BEGIN
SELECT Id, Name,Total
FROM MyTable;
END
ELSE
BEGIN
SELECT Id, Name,Total
FROM MyTable
WHERE Id = 3926;
END
或者你可以在每次运行时强制recomplilation:
SELECT Id, Name,Total
FROM MyTable
WHERE Id = 3926
OR @IncludeAll = 1
OPTION (RECOMPILE);
为了证明这一点。此外,我成立一个非常简单的表格,并用无意义的数据填充:
CREATE TABLE dbo.T (ID INT, Filler CHAR(1000));
INSERT dbo.T (ID)
SELECT TOP 100000 a.Number
FROM master..spt_values a, master..spt_values b
WHERE a.type = 'P'
AND b.Type = 'P'
AND b.Number BETWEEN 1 AND 100;
CREATE NONCLUSTERED INDEX IX_T_ID ON dbo.T (ID);
然后我运行相同的查询4次。
- 随着
@IncludeAll
设置为1,查询计划使用表扫描和计划缓存
- 相同的查询与
@IncludeAll
设置为false,与表扫描计划仍缓存,使得在使用。
- 清除计划的缓存,然后以
@IncludeAll
为假再次运行查询,以便现在编译计划并使用索引查找和书签查找进行存储。
- 运行
@IncludeAll
设置为true。索引查找和查找再次使用。
DECLARE @SQL NVARCHAR(MAX) = 'SELECT COUNT(Filler) FROM dbo.T WHERE @IncludeAll = 1 OR ID = 2;',
@ParamDefinition NVARCHAR(MAX) = '@IncludeAll BIT',
@PlanHandle VARBINARY(64);
EXECUTE sp_executesql @SQL, @ParamDefinition, 1;
EXECUTE sp_executesql @SQL, @ParamDefinition, 0;
SELECT @PlanHandle = cp.Plan_Handle
FROM sys.dm_exec_cached_plans cp
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS st
WHERE st.text LIKE '%' + @SQL;
DBCC FREEPROCCACHE (@PlanHandle); -- CLEAR THE CACHE
EXECUTE sp_executesql @SQL, @ParamDefinition, 0;
EXECUTE sp_executesql @SQL, @ParamDefinition, 1;
DBCC FREEPROCCACHE (@PlanHandle); -- CLEAR THE CACHE
检查执行计划表明,一旦查询已经编译它会重复使用相同的计划,无论参数值,而且它会缓存适合于通过当值计划它首先运行,而不是最灵活的基础。

什么是每一个执行计划? –
WHERE @includeAll = 1 OR(@includeAll = 0 AND Id = 3926)是否有所作为? – UnhandledExcepSean
尝试切换它们的顺序,并添加括号,两者都没有改变。 – waltsj19