SELECT INTO 在執行時之前是否在 TempDB 中保留 #Object 名稱?
整理了一個快速的過程來幫助調試,我遇到了編譯器中的一個似乎是錯誤的問題。
create proc spFoo @param bit as begin if @param = 0 begin select * into #bar from [master].dbo.spt_values -- where number between ... end else begin select top 10 * into #bar from [master].dbo.spt_values order by newid(); end; end;
嘗試上述返回以下錯誤
消息 2714,級別 16,狀態 1,過程 spFoo,第 19 行
數據庫中已經有一個名為“#bar”的對象。
在人類可讀的意義上,proc 似乎很好:只有一條
select into
語句將被執行,因為它們被包裝在if-else
塊內。不過很好,SQL Server 不能確認這些語句在邏輯上是相互排除的。不過,也許更令人困惑的是,當將drop table #foo
放置在 if-else 塊中時錯誤仍然存在(假設會告訴編譯器釋放對象名稱),如下所示。create proc spFoo @param bit as begin select top 1 * into #bar from [master].dbo.spt_values if @param = 0 begin drop table #bar; select * into #bar from [master].dbo.spt_values -- where number between ... end else begin drop table #bar; select top 10 * into #bar from [master].dbo.spt_values order by newid(); end; end;
過程本身很好。我把它吸了起來,寫了
create table #foo( ... )
andinsert #foo ( ... )
語句,我一直試圖跳過select * into
語法。在這一點上,我只是想理解為什麼編譯器會用懶惰的語法對我產生影響。我唯一能想到的是 DDL 命令在 TEMPDB中保留對象名稱。為什麼是粗體字?
create proc spIck as begin create table #ack ( col1 int ); drop table #ack; create table #ack ( colA char( 1 ) ); drop table #ack; end;
這將失敗並顯示與上述相同的錯誤程式碼。但是下面…
create proc spIck as begin create table ack ( col1 int ); drop table ack; create table ack ( colA char( 1 ) ); drop table ack; end;
…成功。上面對原始的 proc 嘗試也是如此。所以…
我的問題是這個
TempDB
與使用者數據庫相比,對象的對象名稱保留有什麼區別(以及為什麼存在) 。我審查過的邏輯查詢處理參考資料和 DDL 命令參考資料似乎都沒有解釋這一點。
這與 TempDB 中的對象名稱保留或執行時無關。這只是解析器無法遵循確保您的程式碼不可能嘗試創建該表兩次的邏輯或程式碼路徑。
Ctrl
請注意,如果您只是點擊 Parse 按鈕 ( + ) ,您會得到完全相同的(非執行時!)錯誤F5
。基本上,如果你有這個:IF 1=1 CREATE TABLE #foo(id1 INT); ELSE CREATE TABLE #foo(id2 INT);
解析器看到這個:
CREATE TABLE #foo(id1 INT); CREATE TABLE #foo(id2 INT);
為什麼它不適用於實際表,包括在 TempDB 中創建的實際使用者表(請注意,它也不是特定於數據庫的)?我可以建議的唯一答案是解析器對#temp 表有一組不同的規則(也有很多其他差異)。如果您想了解更具體的原因,您需要向 Microsoft 開一個案例,看看他們是否會為您提供更多詳細資訊。我猜你會被告知:“這就是它的工作方式。”
這些答案中的更多資訊: