Reputation: 173
Here is the situation:
Procedure 1 creates a temp table (#MYTABLE) and calls Procedure 2. Procedure 2 also tries to create #MYTABLE, with different columns. When Procedure 2 tries to insert data into #MYTABLE, an error happens complaining "Invalid column name". I have two questions about this:
1) Shouldn't the system complain when #MYTABLE is created inside Procedure 2? I understand why it can't object at compilation time, but at runtime I would expect an error.
2) Given that it doesn't complain about the creation, and in fact when you SELECT from #MYTABLE inside Procedure 2, you see the new column, why does it complain about the INSERT?
Below is the code. Uncommenting either INSERT statement will get the error.
(I know a lot of ways to fix this situation, so I don't need responses about that. I just want to understand what's happening.)
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;
Upvotes: 7
Views: 3048
Reputation: 135938
From the Create Table documentation:
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.
Upvotes: 6
Reputation: 176284
1) Shouldn't the system complain when #MYTABLE is created inside Procedure 2? I understand why it can't object at compilation time, but at runtime I would expect an error.
No it shoudn't. You will get 2 local temporary tables see their names:
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
Output:
╔════════════════╦═══════════════╦═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╦════════════╗
║ TABLE_CATALOG ║ TABLE_SCHEMA ║ TABLE_NAME ║ TABLE_TYPE ║
╠════════════════╬═══════════════╬═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╬════════════╣
║ tempdb ║ dbo ║ #MYTABLE____________________________________________________________________________________________________________000000000117 ║ BASE TABLE ║
║ tempdb ║ dbo ║ #MYTABLE____________________________________________________________________________________________________________000000000118 ║ BASE TABLE ║
╚════════════════╩═══════════════╩═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩════════════╝
2) Given that it doesn't complain about the creation, and in fact when you SELECT from #MYTABLE inside Procedure 2, you see the new column, why does it complain about the INSERT?
Because SQL Server get first table definition from outer stored procedure. It has different columns so you will get error during INSERT
Upvotes: 2
Reputation: 453950
1) Shouldn't the system complain when #MYTABLE is created inside Procedure 2? I understand why it can't object at compilation time, but at runtime I would expect an error.
It does complain at compilation time. When it compiles dbo.MYPROC2
it sees that the table exists at the parent scope and is not compatible with the column list you are using. If there was no visible parent object of that name then compilation of that statement would have been deferred until it was executed (after the CREATE TABLE
).
If you were to remove the initial SELECT
from dbo.MYPROC2
and then execute dbo.MYPROC2
first before dbo.MYPROC1
it will likely succeed - as it will already have the cached plan for dbo.MYPROC2
and no need to recompile.
I do not recommend this however unless you enjoy random errors when the plan is removed from cache and the procedures are executed in the wrong order. Best to use unique names.
Upvotes: 2
Reputation: 1384
Well, at the first glance your assumption is OK, but only at the first one.
When you create temporary table named MyTable, SQL Server creates actual table in TEMPDB, which is named something like 'MyTable_____________...._____01D', so when any other piece of code creates table with the same name but in the different scope, Server can make difference of them.
And in your case, you create local temporary tables in two different scopes - two different procedures, never the mind that one is calling another, you CANNOT access table created in second procedure from the first one.
What I would suggest you is to select data from sys.objects, so that you can see there are two actual and different tables created - select name from tempdb..sysobjects where name like 'MYTABLE%'
And last - you use same name and expect to access the "smallest" scope table, but actually Server uses the table that was created first. Assume that SQL server just selects top 1 from sys.objects where scope and name match current ones.
Upvotes: -1