dri*_*ver 2 sql t-sql sql-server
我有SQL命令
exec sp_executesql N'SELECT TOP (10) * FROM mytableView WHERE ([Name]) LIKE (''%'' +
(@Value0) + ''%'') ORDER BY [Id] DESC',N'@Value0 varchar(5)',@Value0='value'
Run Code Online (Sandbox Code Playgroud)
此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'
Run Code Online (Sandbox Code Playgroud)
是否可以重新编译/重新创建/删除/更新执行计划,以使我的SQL命令在没有选项(重新编译)的情况下工作?
我已尝试申请
您可以尝试使用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';
Run Code Online (Sandbox Code Playgroud)
查询提示的MSDN页面指出优化为未知:
指示查询优化器在编译和优化查询时使用统计数据而不是所有局部变量的初始值,包括使用强制参数化创建的参数。
该提示指示优化器使用指定表的总行数除以指定列的不同值的数量(即每个值的平均行数)作为行估计,而不是使用任何特定值的统计信息。正如@GarethD在以下评论中指出的那样:由于这可能会使某些查询受益,而可能会伤害其他查询,因此需要对其进行测试,以查看由此带来的总体收益是否比执行RECOMPILE的成本节省了净额。有关更多详细信息,请查看:未知的优化方式。
只是说一下,取决于数据的分布和传入的值,如果使用的特定值的分布相当能代表大多数可以传入的值(即使与某些永远不会传入的值完全不同),那么您可以使用OPTIMIZE FOR (@Value0 = 'representative value')
而不是来定位该值OPTIMIZE FOR UNKNOWN
。
请注意,只有具有以下条件的查询才需要此查询提示:
以下注释中标识了以下情形,并且并非全部都需要此提示,因此这里是解决每种情况的方法:
select top 80 * from table order by id desc
这里没有传递变量,因此不需要查询提示。
select top 80 * from table where id < @lastid order by id desc
这里传递了一个变量,但是[id]字段本质上是均匀分布的,即使由于某些删除而稀疏,因此也不需要查询提示(或至少不需要)。
SELECT TOP (10) * FROM mytableView WHERE ([Name]) LIKE (''%'' + (@Value0) + ''%'') ORDER BY [Id] DESC
这里传递了一个变量,并且以这样的方式使用该变量:对于不同的值,可能不会显示匹配行的一致数目,特别是由于前导导致无法使用索引%
。OPTION (OPTIMIZE FOR UNKNOWN)
如上所述,这是获得提示的好机会。
如果存在这样一种情况,即传入的变量的匹配行分布有很大不同,但是没有太多可能要传入的值,并且经常重复使用传入的值,则可以将它们串联起来(完成后REPLACE(@var, '''', '''''')
)直接进入动态SQL。这允许这些值中的每一个都有自己单独的但可重用的查询计划。其他变量应像往常一样作为参数发送。
例如,[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;
Run Code Online (Sandbox Code Playgroud)
假设传入了两个不同的@StatusID值(例如1
和2
),则将缓存两个与以下查询匹配的执行计划:
SELECT TOP (10) * FROM myTableView WHERE [StatusID] = 1 AND [OtherField] = @OtherFieldVal;
SELECT TOP (10) * FROM myTableView WHERE [StatusID] = 2 AND [OtherField] = @OtherFieldVal;
归档时间: |
|
查看次数: |
500 次 |
最近记录: |