2015-10-28 91 views
6

這裏內部重複的臨時表的情況是:創建嵌套的存儲過程

步驟1創建臨時表(#MYTABLE),並調用步驟2.步驟2也嘗試創建#MYTABLE,與不同的列。當過程2嘗試將數據插入到#MYTABLE中時,會發生抱怨「列名無效」的錯誤。我對這個兩個問題:內部程序2創建#MYTABLE當

1)如果不是系統抱怨嗎?我明白爲什麼它不能在編譯時反對,但是在運行時我會期待一個錯誤。 2)鑑於它不會抱怨創建,事實上當你在程序2中的#MYTABLE中選擇SELECT時,你會看到新列,爲什麼它會抱怨INSERT?

以下是代碼。取消註釋INSERT語句會得到錯誤。

(我知道了很多辦法來解決這個情況,所以我並不需要有關的反應。我只是想知道發生了什麼。)

IF OBJECT_ID(N'dbo.MYPROC1', N'P') IS NOT NULL 
    DROP PROCEDURE dbo.MYPROC1; 
GO 

CREATE PROCEDURE dbo.MYPROC1 
AS 
    CREATE TABLE dbo.#MYTABLE (Name VARCHAR(256)); 

    SELECT 
     'DO NOTHING 1' AS TABLENAME; 

    EXEC dbo.MYPROC2; 

GO 

IF OBJECT_ID(N'dbo.MYPROC2', N'P') IS NOT NULL 
    DROP PROCEDURE dbo.MYPROC2; 
GO 

CREATE PROCEDURE dbo.MYPROC2 
AS 
    SELECT 
     'INSIDE PROC 2 BEFOREHAND' AS TABLENAME 
     ,* 
    FROM 
     dbo.#MYTABLE; 

    CREATE TABLE dbo.#MYTABLE 
     (
     Name VARCHAR(256) 
     ,LastName VARCHAR(256) 
     ); 

    --INSERT INTO dbo.#MYTABLE 
    --  (Name, LastName) 
    --  SELECT 
    --   'BARACK' 
    --   ,'OBAMA'; 

    SELECT 
     'INSIDE PROC 2 AFTERWARDS' AS TABLENAME 
     ,* 
    FROM 
     dbo.#MYTABLE; 

    --INSERT INTO dbo.#MYTABLE 
    --  (Name, LastName) 
    --  SELECT 
    --   'BARACK' 
    --   ,'OBAMA'; 

    SELECT 
     'DO NOTHING 2' AS TABLENAME; 

GO 

EXEC MYPROC1; 

回答

4

得到錯誤從Create Table文檔:

一個存儲過程或觸發器中創建的本地臨時表可以有相同的名字因爲這是之前創建的臨時表存儲過程或觸發器被調用。但是,如果查詢引用臨時表並且同時存在兩個具有相同名稱的臨時表,則不會定義查詢針對哪個表進行解析。嵌套存儲過程還可以創建臨時表,其名稱與由調用它的存儲過程創建的臨時表相同。但是,對於修改以解析嵌套過程中創建的表,該表必須具有與在調用過程中創建的表相同的結構,具有相同的列名稱。

-1

好,第一眼你的假設是好的,但只在第一個。

當您創建名爲MyTable的臨時表時,SQL Server會在TEMPDB中創建實際的表,該表被命名爲'MyTable _____________...._____ 01D',因此當其他代碼段創建具有相同名稱的表時,不同的範圍,服務器可以區分它們。

而在你的情況,你有兩種不同的範圍創建本地臨時表 - 兩個不同的過程,從來沒有一個被調用另一個頭腦,你不能從第一個訪問第二個過程創建的表。

我建議你是選擇從sys.objects中的數據,這樣就可以看到有創建了兩個實際的和不同的表 - select name from tempdb..sysobjects where name like 'MYTABLE%'

而在去年 - 您使用相同的名稱,並希望訪問「最小的「範圍表,但實際上Server使用先創建的表。假定SQL服務器只從sys.objects中選擇top和1,其中scope和name與當前匹配。

+0

這個答案很好地解釋了問題1,謝謝。但我仍然不明白爲什麼INSERT和SELECT的行爲不同。爲什麼SELECT「看到」較新的表並插入「看到」較舊的表? –

+0

嗯,這是一個很好的問題,我無法回答(我很抱歉,我在回答您的文章時沒有完全閱讀它)。它可能應該指向更有能力的團隊,即那些瞭解SQL Server內部運行方式的人。我將嘗試在不同版本的SQL Server上運行相同的示例並查看它的行爲。你使用哪個版本? – GSazheniuk

+0

「您無法訪問第二個程序中創建的表格。」這是不正確的。臨時表有種動態範圍。它們沒有像變量那樣作用域。一旦創建了表,其他過程就可以讀取和寫入。雖然讀取顯然是陰影。 –

2

1)當在 內創建#MYTABLE時,系統是否應該抱怨過程2?我明白爲什麼它不能在編譯時反對,但在運行時 我會期待一個錯誤。

確實在編譯時抱怨。當它編譯dbo.MYPROC2時,它會發現該表存在於父作用域中,並且與您正在使用的列列表不兼容。如果沒有該名稱的可見父對象,則該語句的編譯將被推遲到執行(在CREATE TABLE之後)。

如果你從dbo.MYPROC2刪除初始SELECT,然後執行dbo.MYPROC2第一dbo.MYPROC1之前,它很可能會成功 - 因爲它已經具有了dbo.MYPROC2高速緩存的計劃,無需重新編譯。

但是,我不建議這樣做,除非您在計劃從緩存中移除並且順序錯誤執行時享受隨機錯誤。最好使用獨特的名稱。

+0

確認,謝謝你指出我的草稿語言「編譯時間」。我應該說「當CREATE PROCEDURE發生時」。 –

2

1)當在 內部創建#MYTABLE時,系統是否應該抱怨過程2?我明白爲什麼它不能在編譯時反對,但在運行時 我會期待一個錯誤。

不,它不應該。您將獲得2個局部臨時表看到自己的名字:

CREATE PROCEDURE dbo.MYPROC1 
AS 
    CREATE TABLE dbo.#MYTABLE (Name VARCHAR(256)); 
    EXEC dbo.MYPROC2; 
GO 

CREATE PROCEDURE dbo.MYPROC2 
AS 
    CREATE TABLE dbo.#MYTABLE(
     Name VARCHAR(256) 
     ,LastName VARCHAR(256)); 

    SELECT * 
    FROM tempdb.INFORMATION_SCHEMA.TABLES 
    WHERE [Table_name] LIKE '%MYTABLE%' 
GO 

SqlFiddleDemo

輸出:

╔════════════════╦═══════════════╦═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╦════════════╗ 
║ TABLE_CATALOG ║ TABLE_SCHEMA ║               TABLE_NAME                ║ TABLE_TYPE ║ 
╠════════════════╬═══════════════╬═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╬════════════╣ 
║ tempdb   ║ dbo   ║ #MYTABLE____________________________________________________________________________________________________________000000000117 ║ BASE TABLE ║ 
║ tempdb   ║ dbo   ║ #MYTABLE____________________________________________________________________________________________________________000000000118 ║ BASE TABLE ║ 
╚════════════════╩═══════════════╩═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩════════════╝ 

2)鑑於它不抱怨的創建,而實際上當你從#MYTABLE裏SELECT 2裏選擇了 時,你會看到新的列, 它爲什麼會抱怨INSERT?

由於SQL Server從外部存儲過程獲取第一個表定義。它有不同的列,因此您將在INSERT