主頁 > 知識(shí)庫 > 淺析SQL Server的嵌套存儲(chǔ)過程中使用同名的臨時(shí)表怪像

淺析SQL Server的嵌套存儲(chǔ)過程中使用同名的臨時(shí)表怪像

熱門標(biāo)簽:南京電銷外呼系統(tǒng)哪家好 地圖標(biāo)注如何弄全套標(biāo) 外呼系統(tǒng)會(huì)封嗎 股票配資電銷機(jī)器人 實(shí)體店地圖標(biāo)注怎么標(biāo) 電銷機(jī)器人 深圳 武漢AI電銷機(jī)器人 在電子版地圖標(biāo)注要收費(fèi)嗎 萬利達(dá)綜合醫(yī)院地圖標(biāo)注點(diǎn)

SQL Server的嵌套存儲(chǔ)過程,外層存儲(chǔ)過程和內(nèi)層存儲(chǔ)過程(被嵌套調(diào)用的存儲(chǔ)過程)中可以存在相同名稱的本地臨時(shí)表嗎?如果可以的話,那么有沒有什么問題或限制呢? 在嵌套存儲(chǔ)過程中,調(diào)用的是外層存儲(chǔ)過程的臨時(shí)表還是自己定義的臨時(shí)表呢? 是否類似高級(jí)語言的變量一樣,本地臨時(shí)表有沒有“作用域“范圍呢?

注意:也可以稱呼為父存儲(chǔ)過程和子存儲(chǔ)過程,外層存儲(chǔ)過程和內(nèi)層存儲(chǔ)過程。這些只是不同的稱呼或叫法而已。我們這里統(tǒng)一使用外層存儲(chǔ)過程和內(nèi)層存儲(chǔ)過程。后續(xù)文章部分不再述說。

我們先來看一個(gè)例子,如下所示,我們構(gòu)造一個(gè)簡單的例子。

IF EXISTS (SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.PRC_TEST') AND OBJECTPROPERTY(object_id, 'IsProcedure') =1)
BEGIN
  DROP PROCEDURE dbo.PRC_TEST
END
GO
CREATE PROC dbo.PRC_TEST
AS
BEGIN
 
  CREATE TABLE #tmp_test(id INT);
 
  INSERT INTO #tmp_test
  SELECT 1;
 
  SELECT * FROM #tmp_test;
 
  EXEC PRC_SUB_TEST
 
  SELECT * FROM #tmp_test
  
 
END
GO
 
IF EXISTS(SELECT 1 FROM sys.objects WHERE object_id= OBJECT_ID(N'dbo.PRC_SUB_TEST' ) AND OBJECTPROPERTY(object_id, 'IsProcedure')=1)
BEGIN
  DROP PROCEDURE dbo.PRC_SUB_TEST;
END
GO
 
CREATE PROCEDURE dbo.PRC_SUB_TEST
AS
BEGIN
  
  CREATE TABLE #tmp_test(name VARCHAR(128));
 
  INSERT INTO #tmp_test
  SELECT name FROM sys.objects
 
  SELECT * FROM #tmp_test;
END
GO
 
EXEC PRC_TEST;

簡單測試似乎正常,并沒有發(fā)現(xiàn)什么問題。如果此時(shí)你就下一個(gè)結(jié)論的話,那么就為時(shí)過早了! 打個(gè)比方,你看見一只天鵝是白色的,如果你下了一個(gè)定論:“所有天鵝都是白色的”,其實(shí)這個(gè)世界真的有黑天鵝,只是你沒有見過而已!如下所示,我們修改一下存儲(chǔ)過程dbo.PRC_SUB_TEST,使用字段名name替換*,如下所示:

IF EXISTS(SELECT 1 FROM sys.objects WHERE object_id= OBJECT_ID(N'dbo.PRC_SUB_TEST' ) AND OBJECTPROPERTY(object_id, 'IsProcedure')=1)
BEGIN
  DROP PROCEDURE dbo.PRC_SUB_TEST;
END
GO
 
CREATE PROCEDURE dbo.PRC_SUB_TEST
AS
BEGIN
  
  CREATE TABLE #tmp_test(name VARCHAR(128));
 
  INSERT INTO #tmp_test
  SELECT name FROM sys.objects
 
  SELECT name FROM #tmp_test;
END
GO

然后重復(fù)上面測試,如下所示,此時(shí)執(zhí)行存儲(chǔ)過程dbo.PRC_TEST的話,就會(huì)報(bào)錯(cuò):“Invalid column name 'name'.”

此時(shí)只要先我執(zhí)行一次存儲(chǔ)過程dbo.PRC_SUB_TEST,然后再去執(zhí)行存儲(chǔ)過程dbo.PRC_TEST就不會(huì)報(bào)錯(cuò)了。而且只要執(zhí)行過一次這個(gè)存儲(chǔ)過程,然后在當(dāng)前會(huì)話或其它任何會(huì)話執(zhí)行dbo.PRC_TEST都不會(huì)報(bào)錯(cuò)了。是否非常讓人迷惑或不解。

EXEC dbo.PRC_SUB_TEST;
 
EXEC PRC_TEST;

如果你要再次重現(xiàn)這個(gè)現(xiàn)象的話,只能通過下面SQL或者刪除/重建存儲(chǔ)過程的方式,才能重現(xiàn)這個(gè)現(xiàn)象。似乎有點(diǎn)幽靈現(xiàn)象的感覺。

DBCC FREEPROCCACHE

關(guān)于這個(gè)現(xiàn)象,官方文檔(詳見參考資料的鏈接地址)有這么一段描述:

A local temporary table created within a stored procedure or trigger can have the same name as a temporary table that was created before the stored procedure or trigger is called. However, if a query references a temporary table and two temporary tables with the same name exist at that time, it is not defined which table the query is resolved against. Nested stored procedures can also create temporary tables with the same name as a temporary table that was created by the stored procedure that called it. However, for modifications to resolve to the table that was created in the nested procedure, the table must have the same structure, with the same column names, as the table created in the calling procedure. This is shown in the following example.

在存儲(chǔ)過程或觸發(fā)器中創(chuàng)建的本地臨時(shí)表的名稱可以與在調(diào)用存儲(chǔ)過程或觸發(fā)器之前創(chuàng)建的臨時(shí)表名稱相同。 但是,如果查詢引用臨時(shí)表,而同時(shí)有兩個(gè)同名的臨時(shí)表,則不定義針對(duì)哪個(gè)表解析該查詢。 嵌套存儲(chǔ)過程同樣可以創(chuàng)建與調(diào)用它的存儲(chǔ)過程所創(chuàng)建的臨時(shí)表同名的臨時(shí)表。但是,為了對(duì)其進(jìn)行修改以解析為在嵌套過程中創(chuàng)建的表,此表必須與調(diào)用過程創(chuàng)建的表具有相同的結(jié)構(gòu)和列名。下面的示例說明了這一點(diǎn)。

CREATE PROCEDURE dbo.Test2
AS
  CREATE TABLE #t(x INT PRIMARY KEY);
  INSERT INTO #t VALUES (2);
  SELECT Test2Col = x FROM #t;
GO
 
CREATE PROCEDURE dbo.Test1
AS
  CREATE TABLE #t(x INT PRIMARY KEY);
  INSERT INTO #t VALUES (1);
  SELECT Test1Col = x FROM #t;
EXEC Test2;
GO
 
CREATE TABLE #t(x INT PRIMARY KEY);
INSERT INTO #t VALUES (99);
GO
 
EXEC Test1;
GO

官方文檔中“同時(shí)有兩個(gè)同名的臨時(shí)表,則不定義針對(duì)哪個(gè)表解析該查詢”這種闡述感覺還是讓人有點(diǎn)迷糊。這里簡單解釋一下,在存儲(chǔ)過程的嵌套調(diào)用中,允許外層過程和內(nèi)層存儲(chǔ)過程中存在相同名字的本地臨時(shí)表,但是在內(nèi)存過程中,如果要對(duì)其進(jìn)行修改或解析(修改很好理解,例如新增索引,增加字段等這類DDL操作;關(guān)于解析,查詢臨時(shí)表,SQL中指定字段名,就需要解析resolve),那么此時(shí)這個(gè)臨時(shí)表必須表結(jié)構(gòu)一致,否則就會(huì)報(bào)錯(cuò)。官方文檔,就是這么一句話,告訴你不行,但是具體原因沒有說。那么我們不妨做一些推測,在存儲(chǔ)過程的嵌套調(diào)用中,是否創(chuàng)建了兩個(gè)本地臨時(shí)表呢?有沒有可能實(shí)際只創(chuàng)建了一個(gè)本地臨時(shí)表呢?出現(xiàn)本地臨時(shí)表重用的情況呢? 那么我們簡單驗(yàn)證一下,如下所示,這里可以判斷實(shí)際上創(chuàng)建了兩個(gè)本地臨時(shí)表。并沒有出現(xiàn)臨時(shí)表重用的情況。

SELECT * 
FROM sys.dm_os_performance_counters
WHERE counter_name LIKE 'Temp Tables Creation Rate%';
 
EXEC PRC_TEST;
 
SELECT * 
FROM sys.dm_os_performance_counters
WHERE counter_name LIKE 'Temp Tables Creation Rate%';

當(dāng)然你可以用下面SQL來進(jìn)行驗(yàn)證,跟上面驗(yàn)證的結(jié)果一致。

IF EXISTS(SELECT 1 FROM sys.objects WHERE object_id= OBJECT_ID(N'dbo.PRC_SUB_TEST' ) AND OBJECTPROPERTY(object_id, 'IsProcedure')=1)
BEGIN
  DROP PROCEDURE dbo.PRC_SUB_TEST;
END
GO
 
 
CREATE PROCEDURE dbo.PRC_SUB_TEST
AS
BEGIN
  
  SELECT * FROM #tmp_test;
 
  SELECT * FROM tempdb.dbo.sysobjects WHERE name LIKE '#tmp_test%'
  CREATE TABLE #tmp_test(name VARCHAR(128));
 
  INSERT INTO #tmp_test
  SELECT name FROM sys.objects
  SELECT * FROM tempdb.dbo.sysobjects WHERE name LIKE '#tmp_test%'
  SELECT * FROM #tmp_test;
END
GO

然后我們來看看臨時(shí)表的“作用域”,抱歉我用這么一個(gè)概念,官方文檔是沒有這個(gè)概念,這個(gè)只是我們思考的一個(gè)方面,細(xì)節(jié)方面沒有必要抬杠。如下所示,我們修改一下存儲(chǔ)過程

IF EXISTS(SELECT 1 FROM sys.objects WHERE object_id= OBJECT_ID(N'dbo.PRC_SUB_TEST' ) AND OBJECTPROPERTY(object_id, 'IsProcedure')=1)
BEGIN
  DROP PROCEDURE dbo.PRC_SUB_TEST;
END
GO
CREATE PROCEDURE dbo.PRC_SUB_TEST
AS
BEGIN
  
  SELECT * FROM #tmp_test;
  CREATE TABLE #tmp_test(name VARCHAR(128));
 
  INSERT INTO #tmp_test
  SELECT name FROM sys.objects
 
  SELECT * FROM #tmp_test;
END
GO

通過實(shí)驗(yàn)驗(yàn)證,我們發(fā)現(xiàn)外層存儲(chǔ)過程的臨時(shí)表在內(nèi)層存儲(chǔ)過程中有效,它的“作用域”是在內(nèi)層存儲(chǔ)過程的同名臨時(shí)表創(chuàng)建之前,這個(gè)跟高級(jí)語言中的全局變量和局部變量作用域有點(diǎn)類似。

既然創(chuàng)建了兩個(gè)本地臨時(shí)表,那么為什么修改或解析的時(shí)候就會(huì)報(bào)錯(cuò)呢? 個(gè)人的一個(gè)猜測是,優(yōu)化器解析過后,在執(zhí)行過程中,解析或修改的時(shí)候,數(shù)據(jù)庫引擎無法判斷或者代碼里面沒有這種邏輯去控制檢索哪一個(gè)臨時(shí)表。有可能是代碼里面的一個(gè)缺陷亦或是某種邏輯原因?qū)е?。上述僅僅是個(gè)人的一個(gè)猜測、推理。如有不足或不對(duì)的地方,敬請(qǐng)指正。

參考資料:

https://docs.microsoft.com/zh-cn/previous-versions/sql/sql-server-2012/ms174979(v=sql.110)?redirectedfrom=MSDN

到此這篇關(guān)于淺析SQL Server的嵌套存儲(chǔ)過程中使用同名的臨時(shí)表怪像的文章就介紹到這了,更多相關(guān)SQL Server嵌套存儲(chǔ)過程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • sqlserver2005利用臨時(shí)表和@@RowCount提高分頁查詢存儲(chǔ)過程性能示例分享
  • sql server2008調(diào)試存儲(chǔ)過程的完整步驟
  • SQLServer2008存儲(chǔ)過程實(shí)現(xiàn)數(shù)據(jù)插入與更新
  • Sql Server 存儲(chǔ)過程調(diào)用存儲(chǔ)過程接收輸出參數(shù)返回值
  • SQLServer存儲(chǔ)過程創(chuàng)建和修改的實(shí)現(xiàn)代碼
  • 獲取SqlServer存儲(chǔ)過程定義的三種方法
  • SqlServer存儲(chǔ)過程實(shí)現(xiàn)及拼接sql的注意點(diǎn)

標(biāo)簽:武威 廣東 泰安 汕頭 濟(jì)寧 濟(jì)源 臺(tái)州 安徽

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《淺析SQL Server的嵌套存儲(chǔ)過程中使用同名的臨時(shí)表怪像》,本文關(guān)鍵詞  淺析,SQL,Server,的,嵌套,存儲(chǔ),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《淺析SQL Server的嵌套存儲(chǔ)過程中使用同名的臨時(shí)表怪像》相關(guān)的同類信息!
  • 本頁收集關(guān)于淺析SQL Server的嵌套存儲(chǔ)過程中使用同名的臨時(shí)表怪像的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章