一个存储过程的多个执行计划取决于使用的参数
Multiple execution plans for one stored procedure depending on used parameters
是否可以根据使用的参数为一个 SQL 过程存储多个执行计划?我的意思是我有一个 SQL 程序。有一些调用此过程的典型模式(一些输入参数保留为 NULL),我不希望每次调用都重新计算计划。
您可以编写一些 sp,每种情况一个,然后从主 sp 中调用它们作为条件调用:
create proc...
if @param1 is null exec sp_1 @param2, param3
但是如果你的案例太多,这不是一个好的解决方法。
在这种情况下,您可以将查询实现为仅使用非空参数构造它的动态代码,请参见此处的示例:Implementing search_orders with a Parameterised Query by Erland sommarskog,代码如下。
这样查询不会每次都重新编译,但是会有很多计划,因为可以构造许多不同的查询,仅结合非空参数(正是您想要的)
CREATE PROCEDURE search_orders_1 -- 1
@orderid int = NULL, -- 2
@fromdate datetime = NULL, -- 3
@todate datetime = NULL, -- 4
@minprice money = NULL, -- 5
@maxprice money = NULL, -- 6
@custid nchar(5) = NULL, -- 7
@custname nvarchar(40) = NULL, -- 8
@city nvarchar(15) = NULL, -- 9
@region nvarchar(15) = NULL, -- 10
@country nvarchar(15) = NULL, -- 11
@prodid int = NULL, -- 12
@prodname nvarchar(40) = NULL, -- 13
@employeestr varchar(MAX) = NULL, -- 14
@employeetbl intlist_tbltype READONLY, -- 15
@debug bit = 0 AS -- 16
-- 17
DECLARE @sql nvarchar(MAX), -- 18
@paramlist nvarchar(4000), -- 19
@nl char(2) = char(13) + char(10) -- 20
-- 21
SELECT @sql = -- 22
'SELECT o.OrderID, o.OrderDate, od.UnitPrice, od.Quantity, -- 23
c.CustomerID, c.CompanyName, c.Address, c.City, -- 24
c.Region, c.PostalCode, c.Country, c.Phone, -- 25
p.ProductID, p.ProductName, p.UnitsInStock, -- 26
p.UnitsOnOrder, o.EmployeeID -- 27
FROM dbo.Orders o -- 28
JOIN dbo.[Order Details] od ON o.OrderID = od.OrderID -- 29
JOIN dbo.Customers c ON o.CustomerID = c.CustomerID -- 30
JOIN dbo.Products p ON p.ProductID = od.ProductID -- 31
WHERE 1 = 1' + @nl -- 32
-- 33
IF @orderid IS NOT NULL -- 34
SELECT @sql += ' AND o.OrderID = @orderid' + -- 35
' AND od.OrderID = @orderid' + @nl -- 36
-- 37
IF @fromdate IS NOT NULL -- 38
SELECT @sql += ' AND o.OrderDate >= @fromdate' + @nl -- 39
-- 40
IF @todate IS NOT NULL -- 41
SELECT @sql += ' AND o.OrderDate <= @todate' + @nl -- 42
-- 43
IF @minprice IS NOT NULL -- 44
SELECT @sql += ' AND od.UnitPrice >= @minprice' + @nl -- 45
-- 46
IF @maxprice IS NOT NULL -- 47
SELECT @sql += ' AND od.UnitPrice <= @maxprice' + @nl -- 48
-- 49
IF @custid IS NOT NULL -- 50
SELECT @sql += ' AND o.CustomerID = @custid' + -- 51
' AND c.CustomerID = @custid' + @nl -- 52
-- 53
IF @custname IS NOT NULL -- 54
SELECT @sql += ' AND c.CompanyName LIKE @custname + ''%''' + @nl -- 55
-- 56
IF @city IS NOT NULL -- 57
SELECT @sql += ' AND c.City = @city' + @nl -- 58
-- 59
IF @region IS NOT NULL -- 60
SELECT @sql += ' AND c.Region = @region' + @nl -- 61
-- 62
IF @country IS NOT NULL -- 63
SELECT @sql += ' AND c.Country = @country' + @nl -- 64
-- 65
IF @prodid IS NOT NULL -- 66
SELECT @sql += ' AND od.ProductID = @prodid' + -- 67
' AND p.ProductID = @prodid' + @nl -- 68
-- 69
IF @prodname IS NOT NULL --70
SELECT @sql += ' AND p.ProductName LIKE @prodname + ''%''' + @nl-- 71
-- 72
IF @employeestr IS NOT NULL -- 73
SELECT @sql += ' AND o.EmployeeID IN' + -- 74
' (SELECT number FROM dbo.intlist_to_tbl(@employeestr))' + @nl
-- 76
IF EXISTS (SELECT * FROM @employeetbl) -- 77
SELECT @sql += ' AND o.EmployeeID IN (SELECT val FROM @employeetbl)' + @nl
-- 79
SELECT @sql += ' ORDER BY o.OrderID' + @nl -- 80
-- 81
IF @debug = 1 -- 82
PRINT @sql -- 83
-- 84
SELECT @paramlist = '@orderid int, -- 85
@fromdate datetime, -- 86
@todate datetime, -- 87
@minprice money, -- 88
@maxprice money, -- 89
@custid nchar(5), -- 90
@custname nvarchar(40), -- 91
@city nvarchar(15), -- 92
@region nvarchar(15), -- 93
@country nvarchar(15), -- 94
@prodid int, -- 95
@prodname nvarchar(40), -- 96
@employeestr varchar(MAX), -- 97
@employeetbl intlist_tbltype READONLY' -- 98
-- 99
EXEC sp_executesql @sql, @paramlist, -- 100
@orderid, @fromdate, @todate, @minprice, -- 101
@maxprice, @custid, @custname, @city, @region, -- 102
@country, @prodid, @prodname, @employeestr, @employeetbl
答案是肯定的,您可以根据输入参数为单个存储过程制定多个执行计划。这称为参数嗅探。
如果有一组参数可以生成足够好的计划,使过程的所有调用都执行良好,一个选项是使用优化提示:
OPTIMIZE FOR ( @variable_name { UNKNOWN | = literal_constant } [ , ...n ] )
这将避免使用需要为所有调用重新编译过程的方法。
是否可以根据使用的参数为一个 SQL 过程存储多个执行计划?我的意思是我有一个 SQL 程序。有一些调用此过程的典型模式(一些输入参数保留为 NULL),我不希望每次调用都重新计算计划。
您可以编写一些 sp,每种情况一个,然后从主 sp 中调用它们作为条件调用:
create proc...
if @param1 is null exec sp_1 @param2, param3
但是如果你的案例太多,这不是一个好的解决方法。
在这种情况下,您可以将查询实现为仅使用非空参数构造它的动态代码,请参见此处的示例:Implementing search_orders with a Parameterised Query by Erland sommarskog,代码如下。
这样查询不会每次都重新编译,但是会有很多计划,因为可以构造许多不同的查询,仅结合非空参数(正是您想要的)
CREATE PROCEDURE search_orders_1 -- 1
@orderid int = NULL, -- 2
@fromdate datetime = NULL, -- 3
@todate datetime = NULL, -- 4
@minprice money = NULL, -- 5
@maxprice money = NULL, -- 6
@custid nchar(5) = NULL, -- 7
@custname nvarchar(40) = NULL, -- 8
@city nvarchar(15) = NULL, -- 9
@region nvarchar(15) = NULL, -- 10
@country nvarchar(15) = NULL, -- 11
@prodid int = NULL, -- 12
@prodname nvarchar(40) = NULL, -- 13
@employeestr varchar(MAX) = NULL, -- 14
@employeetbl intlist_tbltype READONLY, -- 15
@debug bit = 0 AS -- 16
-- 17
DECLARE @sql nvarchar(MAX), -- 18
@paramlist nvarchar(4000), -- 19
@nl char(2) = char(13) + char(10) -- 20
-- 21
SELECT @sql = -- 22
'SELECT o.OrderID, o.OrderDate, od.UnitPrice, od.Quantity, -- 23
c.CustomerID, c.CompanyName, c.Address, c.City, -- 24
c.Region, c.PostalCode, c.Country, c.Phone, -- 25
p.ProductID, p.ProductName, p.UnitsInStock, -- 26
p.UnitsOnOrder, o.EmployeeID -- 27
FROM dbo.Orders o -- 28
JOIN dbo.[Order Details] od ON o.OrderID = od.OrderID -- 29
JOIN dbo.Customers c ON o.CustomerID = c.CustomerID -- 30
JOIN dbo.Products p ON p.ProductID = od.ProductID -- 31
WHERE 1 = 1' + @nl -- 32
-- 33
IF @orderid IS NOT NULL -- 34
SELECT @sql += ' AND o.OrderID = @orderid' + -- 35
' AND od.OrderID = @orderid' + @nl -- 36
-- 37
IF @fromdate IS NOT NULL -- 38
SELECT @sql += ' AND o.OrderDate >= @fromdate' + @nl -- 39
-- 40
IF @todate IS NOT NULL -- 41
SELECT @sql += ' AND o.OrderDate <= @todate' + @nl -- 42
-- 43
IF @minprice IS NOT NULL -- 44
SELECT @sql += ' AND od.UnitPrice >= @minprice' + @nl -- 45
-- 46
IF @maxprice IS NOT NULL -- 47
SELECT @sql += ' AND od.UnitPrice <= @maxprice' + @nl -- 48
-- 49
IF @custid IS NOT NULL -- 50
SELECT @sql += ' AND o.CustomerID = @custid' + -- 51
' AND c.CustomerID = @custid' + @nl -- 52
-- 53
IF @custname IS NOT NULL -- 54
SELECT @sql += ' AND c.CompanyName LIKE @custname + ''%''' + @nl -- 55
-- 56
IF @city IS NOT NULL -- 57
SELECT @sql += ' AND c.City = @city' + @nl -- 58
-- 59
IF @region IS NOT NULL -- 60
SELECT @sql += ' AND c.Region = @region' + @nl -- 61
-- 62
IF @country IS NOT NULL -- 63
SELECT @sql += ' AND c.Country = @country' + @nl -- 64
-- 65
IF @prodid IS NOT NULL -- 66
SELECT @sql += ' AND od.ProductID = @prodid' + -- 67
' AND p.ProductID = @prodid' + @nl -- 68
-- 69
IF @prodname IS NOT NULL --70
SELECT @sql += ' AND p.ProductName LIKE @prodname + ''%''' + @nl-- 71
-- 72
IF @employeestr IS NOT NULL -- 73
SELECT @sql += ' AND o.EmployeeID IN' + -- 74
' (SELECT number FROM dbo.intlist_to_tbl(@employeestr))' + @nl
-- 76
IF EXISTS (SELECT * FROM @employeetbl) -- 77
SELECT @sql += ' AND o.EmployeeID IN (SELECT val FROM @employeetbl)' + @nl
-- 79
SELECT @sql += ' ORDER BY o.OrderID' + @nl -- 80
-- 81
IF @debug = 1 -- 82
PRINT @sql -- 83
-- 84
SELECT @paramlist = '@orderid int, -- 85
@fromdate datetime, -- 86
@todate datetime, -- 87
@minprice money, -- 88
@maxprice money, -- 89
@custid nchar(5), -- 90
@custname nvarchar(40), -- 91
@city nvarchar(15), -- 92
@region nvarchar(15), -- 93
@country nvarchar(15), -- 94
@prodid int, -- 95
@prodname nvarchar(40), -- 96
@employeestr varchar(MAX), -- 97
@employeetbl intlist_tbltype READONLY' -- 98
-- 99
EXEC sp_executesql @sql, @paramlist, -- 100
@orderid, @fromdate, @todate, @minprice, -- 101
@maxprice, @custid, @custname, @city, @region, -- 102
@country, @prodid, @prodname, @employeestr, @employeetbl
答案是肯定的,您可以根据输入参数为单个存储过程制定多个执行计划。这称为参数嗅探。
如果有一组参数可以生成足够好的计划,使过程的所有调用都执行良好,一个选项是使用优化提示:
OPTIMIZE FOR ( @variable_name { UNKNOWN | = literal_constant } [ , ...n ] )
这将避免使用需要为所有调用重新编译过程的方法。