日期:2013-01-30  浏览次数:20420 次

下面,考虑以下 T-SQL 代码段:-- dbo.someTable will be used to populate a temp table
-- subsequently.
create table dbo.someTable (a int not null, b int not null)
go
declare @i int
set @i = 1
while (@i <= 2000)
begin
  insert into dbo.someTable values (@i, @i+5)
  set @i = @i + 1
end
go
 
-- This is the stored procedure of main interest.
create procedure dbo.AlwaysRecompile
as 
set nocount on
 
-- create a temp table
create table #temp1(c int not null, d int not null)
 
select count(*) from #temp1
 
-- now populate #temp1 with 2000 rows
insert into #temp1
select * from dbo.someTable
 
-- create a clustered index on #temp1
create clustered index cl_idx_temp1 on #temp1(c)
 
select count(*) from #temp1
go

  在 SQL Server 2000 中,当初次执行这个存储过程时,将对第一个“select”语句生成第一个 SP:Recompile 事件。这是一次延迟编译,不是真正的重新编译。第二个 SP:Recompile 事件针对第二个“select”。当发生第一次重新编译时,第二个“select”也会被编译,由于在 SQL Server 2000 中,编译是在批处理级别上进行的。然后,在执行时,#temp1 的架构因新建的聚集索引而发生了变化。所以,产生第二个 SP:Recompile 的缘由是架构更改。

因行修正次数而导致的重新编译

  考虑下方存储过程及其执行。

use AdventureWorks  -- or say "use pubs" on SQL Server 2000
go
create procedure RowCountDemo
as
begin
  create table #t1 (a int, b int)
  declare @i int
  set @i = 0  while (@i < 20)
  begin
    insert into #t1 values (@i, 2*@i - 50)
    select a
    from #t1 
    where a < 10 or ((b > 20 or a >=100) and (a < 10000))
    group by a
 
    set @i = @i + 1
  end
end
go
exec RowCountDemo
go

  回想一下,当表在计算阈值时为空,临时表的重新编译阈值为 6。当执行 RowCountDemo 时,在 #t1 包含整 6 行后,可观察到与“statistics changed”(统计被更改)相关的重新编译。通过更改“while”循环的上限,可观察到更多的重新编译。

因 SET 选项更改而导致的重新编译

  考虑下列存储过程。

use AdventureWorks
go
create procedure SetOptionsDemo as
begin
  set ansi_nulls off
  select p.Size, sum(p.ListPrice)
  from Production.Product p 
     inner join Production.ProductCategory pc
     on p.ProductSubcategoryID = pc.ProductCategoryID
  where p.Color = 'Black'
  group by p.Size
end
go
exec SetOptionsDemo  -- causes a recompilation
go
exec SetOptionsDemo  -- does not cause a recompilation
go

  当执行 SetOptionsDemo 时,在“ansi_nulls”为 ON 的情况下编译“select”查询。当 SetOptionsDemo 开始执行时,该 SET 选项的值将由于“set ansi_nulls off”而发生变化,因此已编译的查询计划将不再“无效”。所以,将在“ansi_nulls”为 OFF 的情况下进行重新编译。第二次执行不会导致重新编译,由于已缓存的计划将在“ansi_nulls”为 OFF 的情况下进行编译。

表明 SQL Server 2005 所需的重新编译较 SQL Server 2000 多的另一个示例

  考虑下列存储过程。

use AdventureWorks   -- say "use pubs" on SQL Server 2000
go
create procedure CreateThenReference as
begin
  -- create two temp tables
  create table #t1(a int, b int)
  create table #t2(c int, d int)
  -- populate them with some data
  insert into #t1 values (1, 1)
  insert into #t1 values (2, 2)
  insert into #t2 values (3, 2)
  insert into #t2 values (4, 3)
     -- issue two queries on them
  select x.a, x.b, sum(y.c)
  from #t1 x inner join #t2 y on x.b = y.d
  group by x.b, x.a
  order by x.b
 
  select * 
  from #t1 z cross join #t2 w
  where w.c != 5 or w.c != 2
end
go
exec CreateThenReference
go

  在 SQL Server 2005 中,CreateThenReference 的第一次执行导致了六项语句级重新编译:其中有四项针对“insert”语句,有两项针对“select”查询。当该存储过程开始执行时,最后的查询计划不包含针对“insert”或“select”语句的计划,由于其所援用(临时表 #t1 和 #t2)的对象还不存在。创建了 #t1 和 #t2 之后,将编译“insert”和“select”的查询计划,而这些编译被视为重新编译。在 SQL Server 2000 中,由于整个存储过程被立即重新编译,因此仅发生一次(存储过程级)重新编译——第一个“insert”开始执行时所引发的重新编译。这时,整个存储过程都被重新编译,而由于 #t1 and #t2 曾经存在,可一次性对后续的“insert”和“select”进行编译。不言而喻,通过添加更多援用诸如 #