開發中,我們經常用Create Procedure命令創建存儲過程,而在創建過程時實際發生的是,Query Analyzer檢查其語法,檢查完畢并正確后將其插入系統表syscomments中,而在過程中引用的對象名稱在該過程被執行之前不被解析,這個技術叫做滯后名稱解析。然而,這個技術卻并不是和我們想象的一樣,它也有鞭長莫及的地方。下面來看一個過程:
CREATE PROC testp @var int
AS
IF @var=1
CREATE TABLE #mytemp(k1 int identity,c1 int)
ELSE
CREATE TABLE #mytemp(k1 int identity,c1 varchar(2))
INSERT #mytemp DEFAULT VALUES
SELECT c1 FROM #mytemp
GO
當編譯該過程時,出現以下錯誤:
sql1.JPG" alt=""/>
也許你會問,@var不可能既等于1又不等于1,那問什麼會出現以上的錯誤呢?大家不要頭暈,現在是在檢查語法的時候,檢查語法是逐行向下的,編譯器不會管你的@var等不等于1,它只檢查語法錯誤!可能你又要說:上面不是說對象名稱滯后解析嗎?臨時表就是一個對象啊,而#mytemp是它的名稱,既然是滯后解析,那就是忽略該名稱不解析,怎麼可能會出現這種錯誤,別急,問題就出在這里!對比看一下下面的過程:
CREATE PROC testp2 @var int
AS
IF @var=1
CREATE TABLE tempdb..mytemp(k1 int identity,c1 int)
ELSE
CREATE TABLE tempdb..mytemp(k1 int identity,c1 varchar(2))
INSERT mytemp DEFAULT VALUES
SELECT c1 FROM mytemp
GO
奇怪的事情發生了,創建這個過程順利地通過,這里只是把臨時表替換成了一個永久性表,并沒做其他任何改變,難道SQL Server在乎創建的是臨時表還是永久性表,呵呵?發生的事情似乎是這樣的,在將過程插入syscomments之前(存儲過程編譯后存放于系統表syscomments中), SQL Server參照臨時表解析CREATE TABLE,也許你會說將臨時表換成一個table類型的變量就不會出現這樣的問題,可是事實是不行,數據類型table同樣受此局限。似乎是從SQL Server 7.0開始,支持永久性表的滯后名稱解析,但不支持臨時表的滯后名稱解析。但不管是哪一種情況,第一個代碼都無法執行,下面采取一個迂回策略來解決這個問題,請看:
CREATE PROC testp @var int
AS
CREATE TABLE #mytemp(k1 int identity)
IF @var=1
ALTER TABLE #mytemp ADD c1 int
ELSE
ALTER TABLE #mytemp ADD c1 varchar(2)
INSERT #mytemp DEFAULT VALUES
EXEC('SELECT c1 FROM #mytemp')
GO
在這里只創建表一次,然后修改它,注意`EXEC('SELECT c1 FROM #mytemp')'
這條語句是必要的,因爲新添加的列對于添加它的過程并非立即可見,如果去掉EXEC執行該過程時,將會顯示這樣的錯誤:
再説一次,新添加的列對于添加它的過程并非立即可見!然而以上的代碼卻帶來了一個性能問題,因爲任何創建臨時表并近一步處理它的存儲過程都將導致該過程的執行計劃重新編譯,這對于高吞吐量環境中的大型過程而言,性能會大打折扣!以下過程將解決這個問題:
CREATE PROC test4
AS
INSERT #temp DEFAULT VALUES
SELECT c1 FROM #temp
GO
CREATE PROC test3
AS
CREATE TABLE #temp (k1 int identity,c1 varchar(2))
EXEC dbo.test4
GO
CREATE PROC test2
AS
CREATE TABLE #temp (k1 int identity,c1 int)
EXEC dbo.test4
GO
CREATE PROC test @var int
AS
IF @var=1
EXEC dbo.test2
ELSE
EXEC dbo.test3
GO
復雜性提高了,但是還是可以解決一些問題,另外,之所以在第二和第三個過程中冗余地調用第四個過程,是因爲臨時表一旦超過其作用域就自動被刪除!