1. 程式人生 > >sqlserver 儲存過程中使用臨時表到底會不會導致重編譯

sqlserver 儲存過程中使用臨時表到底會不會導致重編譯

曾經在網路上看到過,SqlServer的儲存過程中使用臨時表,會導致執行計劃無法重用,

執行時候會導致重編譯的這麼一個說法,自己私底下去做測試的時候,根據profile的跟蹤結果,

如果不是統計資訊變更導致導致的重編譯,單單是使用臨時表,並不會導致重編譯,

但是對於一些特殊的情況,又確實會出現重編譯的,

為了弄清楚這個問題,查閱了大量的資料,才把這個問題弄清楚,這裡特意記錄下來,希望武斷地認為儲存過程中使用了臨時表就會導致重編譯的這個觀點得到糾正。

首先進行下面的測試,我們知道,導致臨時表重編譯的因素之一就是統計資訊的變化,統計資訊的變化依賴於往臨時表中寫入的資料量,

首選我要控制插入臨時表中的資料量不超過統計資訊更新而導致重編譯的閥值,先排除統計資訊的變更導致重編譯,

看看僅僅是多次執行SP,是否因為儲存過程中有了臨時表而會產生重編譯

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 --首選建立一個表,供儲存過程中測試使用 create table test1 ( id int identity(1,1), name varchar(50) ) --插入10000條測試資料 insert into test1 values (NEWID()) go 10000 --建立一個儲存過程,其中儲存過程中定義了一個臨時表,根據引數,往臨時表中寫入資料 create proc testRecompile(@i 
int) as begin create table #t (id int,name varchar(50)) insert into #t select id,name from test1 where id<@i select from #t end

那麼就開始執行這個SP,然後監控profile,看看第一次執行,以及除了第一次執行之後,到底有沒有發生重編譯

1 2 3 4 --第一次執行,代入引數1 exec testRecompile 1 --第二次執行,代入引數2 exec testRecompile 2

下面是profile的截圖,可以很清楚地看到,第一次執行之後,再次執行SP的時候,沒有發生重編譯的動作,也就是說重用了第一次的執行計劃快取

這裡解釋兩個問題,

1,第一次執行的時候,為什麼不是因為架構更改導致的重編譯,而是Deferred Complie?

2,第二次執行的時候,為什麼沒有重編譯,因為臨時表是每次執行的時候建立的啊,肯定是更改了架構(change schema)了,為什麼沒有重編譯?

首先,說明第一個問題,

1,第一次執行的時候,當儲存過程testRecompile編譯的時候,

  插入語句(insert into #t select id,name from test1 where id<@i)和查詢語句(select * from #t),

  因為#t表還沒有被建立,因為這兩句並沒有被編譯,

  編譯的時候的執行計劃並沒有完全完成,

  當這個儲存過程執行的時候,臨時表才被建立,此時才真正的開始編譯臨時表物件的語句,這個編譯的過程是執行的時候完成的,而不是純粹的編譯階段完成的

  所以這是Deferred Complie,也即是執行時才進行的編譯,就是所謂的延遲編譯(Deferred Complie)。

2,第二個問題,重新執行臨時表的時候,按道理,因為建立了臨時表,必然導致架構的更改,為什麼沒有重編譯?

  這個是因為,儲存過程中使用了臨時表,對臨時表的使用是引用其“名稱”(比如這裡的#t),而非ID(從臨時資料庫中查詢sys.sysobjects)

  雖然多個會話同時執行這個SP的話,每個會話都會生成一個臨時表,每個會話生成的臨時表的ID都是不同的,

  但是要注意的是,儲存過程中並沒有直接使用臨時表物件的ID,而是臨時表名字本身,

  第一次執行之後,快取的執行計劃與第二次執行時一樣的,所以第二次執行這個SP可以重用這個第一次生成的執行計劃,

上面說了,在某些情況下,儲存過程中使用臨時表會導致重編譯,這是在什麼情況下發生的呢?

因為在某些情況下,要先生成臨時表,然後以動態sql的方式去執行一段有臨時表參與的sql,此時對於臨時表的引用是引用其ID,而不是名稱

這個要歸結於對於臨時表的呼叫方式,當儲存過程中定義了臨時表,用exec或者是sp_executesql的方式呼叫的時候,這兩種執行sql的方式相當於新建了會話,

此時因為不同回話之間,同一個臨時表生成的ID是不同的,此時才會導致儲存過程中發生sechme change的重編譯

上程式碼

1 2 3 4 5 6 7 8 9 10 11 12 13 create proc testRecompile2(@i int) as begin create table #t (id int,name varchar(50)) insert into #t select id,name from test1 where id<[email protected] exec('select * from #t') end DBCC FREEPROCCACHE --第一次執行,代入引數1 exec testRecompile2 1 --第二次執行,代入引數2 exec testRecompile2 2

在儲存過程中建立了臨時表,以exec或者sp_executesql的方式執行臨時表的sql的時候,才會發生因為schema change導致的重編譯,

因為這兩種方式執行sql,相當於新建會話去執行sql,此時對於臨時表的引用,是引用臨時表生成的ID,不同會話之間的臨時表物件的ID是不同的,所以無法重用執行計劃,會發生重編譯

相關推薦

sqlserver 儲存過程使用臨時到底導致編譯

曾經在網路上看到過,SqlServer的儲存過程中使用臨時表,會導致執行計劃無法重用, 執行時候會導致重編譯的這麼一個說法,自己私底下去做測試的時候,根據profile的跟蹤結果, 如果不是統計資訊變更導致導致的重編譯,單單是使用臨時表,並不會導致重編譯, 但是對於

Sqlserver 遊標&儲存過程&臨時混合使用例項

通過巢狀,根據表中記錄的表名與列名到指定位置取值。 --宣告儲存過程 ProPIMS if (exists (select * from sys.objects where name = 'ProPIMS')) drop proc ProPIMS go create procedure

SQLSERVER儲存過程查詢資料某列存在空值

CREATE PROCEDURE [dbo].[PRO_CHK_COLUMNS_VALUES] -- Add the parameters for the stored procedure here @TableName nvarchar(50) , @colum

SQLSERVER 儲存過程的事務

1 CREATE PROCEDURE ProcedureNamme 2 AS 3 BEGIN 4 SET NOCOUNT ON; 5 6 BEGIN TRY---------------------開始捕捉異常 7 BEGIN TRAN-------

sqlserver儲存過程利用遊標遍歷結果集簡單示例及查詢資料庫時間

本例項用的是sqlserver2005   在儲存過程中遍歷結果,和查詢資料庫中所有 儲存過程名稱 加以處理以文字形式輸出 原始碼: drop procedure findName create procedure findName--建立一個名為findName的儲存過

Oracle儲存過程臨時的建立、刪除,變數的定義和使用

  create or replace procedure Test_GetOaUserInfo  as   --authid current_user操作當前儲存過程的當前使用者,否則提示許可權不足,但是這樣儲存過程這能執行一次   --,或者GRANT CREATE A

儲存過程記錄內容之間的複製

現在有表A,表B, 要把B表中的某條記錄取出來,然後選取指定的欄位複製到表A中,SQL語法如下declare @Id int @Id = SCOPE_IDENTITY() insert into T

SQLServer 儲存過程使用事務

儲存過程SQL: create proc ***** @GoodsId int, @Number int, @StockPrice money, @SupplierId int, @EmpId in

sqlserver儲存過程的迴圈

用遊標,和WHILE可以遍歷您的查詢中的每一條記錄並將要求的欄位傳給變數進行相應的處理 DECLARE @A1 VARCHAR(10), @A2 VARCHAR(10), @A3 INT DECLARE YOUCURNAME CURSOR FOR SELECT A1,A2,A3 FROM YOUT

mysql儲存過程建立臨時,從別的賦值給這個臨時

DELIMITER // CREATE PROCEDURE baseweb.test16() BEGIN     DROP TABLE d;     CREATE TEMPORARY TABLE d(id INT,numbers VARCHAR(50));     INSE

sqlserver儲存過程where條件引數為空加入篩選

CREATE PROCEDURE "dbo"."TEST_PRC"( @no_in VARCHAR(20)

Oracle儲存過程ORA-00942: 或檢視存在問題解決

今天在寫儲存過程的時候,發現一個很詭異的問題,儲存過程裡就一個最簡單的SELECT動態SQL語句。編寫完成以後,執行這個儲存過程,總是提示ORA-00942: table or view does

SQL Server資料庫的儲存過程定義的臨時,真的有必要顯式刪除(drop table #tableName)嗎?

問題背景 在寫SQL Server儲存過程中,如果儲存過程中定義了臨時表,有些人習慣在儲存過程結束的時候一個一個顯式地刪除過程中定義的臨時表(drop table #tName),有些人又沒有這個習慣,對於不明真相的群眾或者喜歡思考的人會問,儲存過程中定義的臨時表,最後要不要主動刪除,為什麼

在oracle儲存過程建立臨時

在oracle的儲存過程中,不能直接使用DDL語句,比如create、alter、drop、truncate等。 那如果我們想在儲存過程中建立一張臨時表就只能使用動態sql語句了: create o

關於sql server 在儲存過程建立臨時,並往臨時插入資料時出現亂碼的問題

  此前在客戶一個繁體系統的sql  server中執行儲存過程的時候出現亂碼,經查發現建立臨時表後,往臨時表中插入資料時得到的資料是亂碼的,所以檢視建立表的sql指令碼,經查需要將變數型別設定為  

如何解決儲存過程關聯臨時時快時慢的情況

     呼叫一個儲存過程,同樣的條件,資料庫負載也是正常的情況下,時快時慢。原因是資料庫無法獲取臨時表的統計資訊,導致執行計劃會走錯。之前寫過一個帖子,被臨時表坑了,http://blog.csdn.net/stevendbaguo/article/details/399

oracle儲存過程使用臨時

1、儲存過程中動態建立表失敗,提示許可權問題,execute immediate 'create global temporary t1 as select * from table1';  解決:  1-授權 create any table  2-儲存過程中加AUT

儲存過程建立臨時,為何沒有許可權?

oracle在儲存過程中建立臨時表,執行execute immediate str;報許可權不夠,但是複製出這個sql,直接在sql視窗執行成功,原因如下: 在儲存過程中的所有操作都必須直接顯式授權,不能通過角色授權。你必須把 CREATE TABLE 許可權顯式授予建立 

Mysql儲存過程(六)——儲存過程使用臨時

Mysql 不支援陣列。但有時候需要組合幾張表的資料,在儲存過程中,經過比較複雜的運算獲取結果直接輸出給呼叫方,比如符合條件的幾張表的某些欄位的組合計算。 Mysql 臨時表可以解決這個問題。 臨時表:只有在當前連線情況下, TEMPORARY 表才是可見的。當連線關閉時

SQL Server的巢狀儲存過程使用同名的臨時怪像淺析

     SQL Server的巢狀儲存過程,外層儲存過程和內層儲存過程(被巢狀呼叫的儲存過程)中可以存在相同名稱的本地臨時表嗎?如果可以的話,那麼有沒有什麼問題或限制呢? 在巢狀儲存過程中,呼叫的是外層儲存過程的臨時表還是自己定義的臨時表呢? 是否類似高階語言的變數一樣,本地臨