我有SQL命令
exec sp_executesql N'SELECT TOP (10) * FROM mytableView WHERE ([Name]) LIKE (''%'' + (@Value0) + ''%'') ORDER BY [Id] DESC',N'@Value0 varchar(5)',@Value0='value'
此sql命令将在22秒后执行。我发现发生这种情况是因为我有一个参数嗅探。如果将其添加到SQL命令 选项(重新编译)的 末尾,则它的工作速度很快:Managements studio中显示了0秒
exec sp_executesql N'SELECT TOP (10) * FROM mytableView WHERE ([Name]) LIKE (''%'' + (@Value0) + ''%'') ORDER BY [Id] DESC option(recompile)',N'@Value0 varchar(5)',@Value0='value'
是否可以重新编译/重新创建/删除/更新执行计划,以使我的SQL命令在没有选项(重新编译)的情况下工作?
我已尝试申请
您可以尝试使用OPTIMIZE FOR UNKNOWN提示,而不是RECOMPILE:
OPTIMIZE FOR UNKNOWN
RECOMPILE
exec sp_executesql N'SELECT TOP (10) * FROM mytableView WHERE ([Name]) LIKE (''%'' + (@Value0) + ''%'') ORDER BY [Id] DESC option(OPTIMIZE FOR UNKNOWN);', N'@Value0 varchar(5)', @Value0 = 'value';
查询提示的MSDN页面指出“优化未知”:
指示查询优化器在编译和优化查询时使用统计数据代替所有局部变量的初始值,包括使用强制参数化创建的参数。
此提示指示优化器使用 指定表 的 总行数 除以 指定列的不同值 的 数量 (即每个值的平均行数)作为行估计,而不是使用任何特定值的统计信息。正如@GarethD在以下评论中指出的那样:由于这可能会使某些查询受益,而可能伤害其他查询,因此需要对其进行测试,以查看由此带来的总收益是否是节省的净成本,而不是执行RECOMPILE的成本。有关更多详细信息,请查看:如何优化未知的工作原理。
只是说明一下,取决于数据的分布和传入的值,如果使用的特定值的分布相当能代表大多数可以传入的值(即使与某些永远不会传入的值完全不同),那么您可以使用OPTIMIZE FOR (@Value0 = 'representative value')而不是来定位该值OPTIMIZE FOR UNKNOWN。
OPTIMIZE FOR (@Value0 = 'representative value')
请注意,只有具有以下条件的查询才需要此查询提示:
以下注释中标识了以下情形,并且并非全部都需要此提示,因此这里是解决每种情况的方法:
select top 80 * from table order by id desc 这里没有传递变量,因此不需要查询提示。
select top 80 * from table order by id desc
select top 80 * from table where id < @lastid order by id desc 这里传递了一个变量,但是[id]字段本质上是均匀分布的,即使由于某些删除而稀疏,因此也不需要查询提示(或至少不需要)。
select top 80 * from table where id < @lastid order by id desc
SELECT TOP (10) * FROM mytableView WHERE ([Name]) LIKE (''%'' + (@Value0) + ''%'') ORDER BY [Id] DESC 这里传递了一个变量,并且以这样的方式使用该变量:对于不同的值,可能不会显示匹配行的一致数目,特别是由于前导导致无法使用索引%。OPTION (OPTIMIZE FOR UNKNOWN)如上所述,这是获得提示的好机会。
SELECT TOP (10) * FROM mytableView WHERE ([Name]) LIKE (''%'' + (@Value0) + ''%'') ORDER BY [Id] DESC
%
OPTION (OPTIMIZE FOR UNKNOWN)
如果存在这样一种情况,即传入的变量的匹配行分布有很大不同,但是没有太多可能要传入的值,并且经常重复使用传入的值,则可以将它们串联起来(完成后REPLACE(@var, '''', ''''''))直接进入动态SQL。这允许这些值中的每一个都有自己单独的但可重复使用的查询计划。其他变量应像往常一样作为参数发送。
REPLACE(@var, '''', '''''')
例如,[StatusID]的查找值将只有几个可能的值,并且它们将被频繁重用,但是每个特定的值都可以匹配行数截然不同的行。在这种情况下,如下所示将允许不需要RECOMPILE或OPTIMIZE FOR UNKNOWN提示的单独执行计划,因为每个执行计划都将针对该特定值进行优化:
IF (TRY_CONVERT(INT, @StatusID) IS NULL) BEGIN ;THROW 50505, '@StatusID was not a valid INT', 55; END; DECLARE @SQL NVARCHAR(MAX); SET @SQL = N'SELECT TOP (10) * FROM myTableView WHERE [StatusID] = ' + REPLACE(@StatusID, N'''', N'''''') -- really only needed for strings + N' AND [OtherField] = @OtherFieldVal;'; EXEC sp_executesql @SQL, N'@OtherFieldVal VARCHAR(50)', @OtherFieldVal = @OtherField;
假设传入了两个不同的@StatusID值(例如1和2),则将缓存两个与以下查询匹配的执行计划:
1
2
* `SELECT TOP (10) * FROM myTableView WHERE [StatusID] = 1 AND [OtherField] = @OtherFieldVal;` * `SELECT TOP (10) * FROM myTableView WHERE [StatusID] = 2 AND [OtherField] = @OtherFieldVal;`