2015-10-28 93 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