先看看下面这个存储过程:
CREATE PROCEDURE List_orders_12 @custid nchar(5),
@fromdate datetime,
@todate datetime AS
SELECT *
FROM Orders
WHERE CustomerID = @custid
AND OrderDate BETWEEN @fromdate AND @todate
在Northwind库中,有两个索引,一个是基于CustomerID列,一个是基于OrderDate列。假设订单在客户之间变动很频繁。很多客户可能每年只有少数几个订单。但是某些客户却又很多。
在Northwind库中,最活跃的用户是SAVEA,有31个订单,而CENTC只有一个,那么运行下面语句:
use Northwind
go
EXEC List_orders_12 'SAVEA', '19970811', '19970811'
go
sp_recompile List_orders_12
go
EXEC List_orders_12 'CENTC', '19960101', '19961231'
注意,对于ASVEA,我们仅查询一天的订单,但是对于CENTC,我们查询一年的数据,你可能想到,这两个调用应该使用不同的索引,下面是实际执行计划:
第一个查询中,SQL Server使用了OrderDate上的索引,因为它具有最高选择度。而第二个查询就不一样,因为这时候CustomerID具有最高选择度,所以SQL Server使用上面的索引。
其中一个解决这种问题的方案是使用RECOMPILE查询提示,每次调用时都触发重编译:
CREATE PROCEDURE List_orders_12 @custid nchar(5),
@fromdate datetime,
@todate datetime AS
SELECT *
FROM Orders
WHERE CustomerID = @custid
AND OrderDate BETWEEN @fromdate AND @todate
OPTION (RECOMPILE)
使用这种方式,SQL Server每次都会对这个“语句”进行重编译。除此之外,还可以让SQL Server重编译整个存储过程:
CREATE PROCEDURE List_orders_12 @custid nchar(5),
@fromdate datetime,
@todate datetime WITH RECOMPILE AS
对于这个存储过程,使用哪种方式都无所谓,因为它是单语句的。但是对于代码很长的存储过程,使用WITH RECOMPILE不是最好的方式,因为这样会引起整个存储过程重编译,增加重编译开销。另外关于WITH RECOMPILE的一个特性,就是计划不会存入缓存,但是使用OPTION (RECOMPILE)就会存入计划缓存中。
在很多情况下,每次强制重编译一般不会出现问题,但是有些情况下就会有问题:
- 存储过程被调用的频率很高,重编译会明显影响系统性能。
- 查询非常复杂并且编译时间已经明显影响了响应时间。
更准确地说,当强制重编译的方式总能奏效时,那么意味着它并不是最佳解决方案。事实上,在这一节的例子里面,可能不是很典型的“在应用程序中慢,在SSMS中快”的情况。因为不同的使用模式会引起不同的性能问题。所以你应该继续看下去,说不定会发现案例中的情况正如你遇到的那样。