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;
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.
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.
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
SqlFiddleDemo
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
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.
Related
I am running into an issue with creating temp tables in Sybase db. We have a sql where we create a temp table, insert/update it and do a select * from it at the end of get some results. We are invoking this sql from the service layer using spring jdbc tmplate. The first run works fine, but the next subsequesnt runs fails with error
cannot create temporary table <name>. Prefix name is already in use by another temorary table
This is how I am checking if table exists:
if object_id('#temp_table') is not null
drop table #temp_table
create table #temp_table(
...
)
Anything I am missing here?
Might not be a great response, but I also have that problem and I have 2 ways around it.
1. Do the IF OBJECT_ID Drop Table as a separate execute prior to the query
2. Do the Drop Table without the IF OBJECT_ID() right after your query.
You are really close but temp tables require using the db name before too.
IF OBJECT_ID('tempdb..#Results') IS NOT NULL
DROP TABLE #Results
GO
It would be the same if you were checking if a user table in another database existed.
IF OBJECT_ID('myDatabase..myTable') IS NOT NULL
DROP TABLE myDatabase..myTable
GO
NOTE: A bit more info on BigDaddyO's first suggestion ...
The code snippet you've provided, when submitted as a SQL batch, is parsed as a single unit of work prior to the execution. Net result is that if #temp_table already exists when the batch is submitted, then the compilation of the create table command will generate the error. This behavior can be seen in the following example:
create table #mytab (a int, b varchar(30), c datetime)
go
-- your code snippet; during compilation the 'create table' generates the error
-- because ... at the time of compilation #mytab already exists:
if object_id('#mytab') is not NULL
drop table #mytab
create table #mytab (a int, b varchar(30), c datetime)
go
Msg 12822, Level 16, State 1:
Server 'ASE200', Line 3:
Cannot create temporary table '#mytab'. Prefix name '#mytab' is already in use by another temporary table '#mytab'.
-- same issue occurs if we pull the 'create table' into its own batch:
create table #mytab (a int, b varchar(30), c datetime)
go
Msg 12822, Level 16, State 1:
Server 'ASE200', Line 1:
Cannot create temporary table '#mytab'. Prefix name '#mytab' is already in use by another temporary table '#mytab'.
As BigDaddyO has suggested, one way to get around this is to break your code snippet into two separate batches, eg:
-- test/drop the table in one batch:
if object_id('#mytab') is not NULL
drop table #mytab
go
-- create the table in a new batch; during compilation we don't get an error
-- because #mytab does not exist at this point:
create table #mytab (a int, b varchar(30), c datetime)
go
I need to execute some dynamic SQL that will return 2 result sets and store the result sets in a table variable.
Let's say I have 2 tables (crappy schema but illustrates my issue)
Table1:
ItemID int
ItemName nvarchar(50)
Table2:
ItemId int
Quantity int
I generate some dynamic sql that looks like this:
DECALRE #sql varchar(max);
SET #sql = 'SELECT * FROM Table1; SELECT * from Table2';
Then I create my table variables:
DECALRE #tbl1 TABLE (ItemId int, ItemName nvarchar(50))
DECALRE #tbl2 TABLE (ItemId int, Quantity int)
Then I want to execute that dynamic SQL and insert the results into the table variables I just declared. If there was just one result set in the dynamic sql I could simply just run this:
INSERT into #tbl1execute ('SELECT * FROM Table1;')
However, this obviously fails as soon as I use the #sql parameter that will return multiple result sets. Is this even possible?
Use temporary tables (not table variables):
CREATE TABLE #Table1(...)
CREATE TABLE #Table2(...)
DECLARE #MyDynamicSql NVARCHAR(MAX) = N'
INSERT INTO #Table1(...)
SELECT ...
INSERT INTO #Table2(...)
SELECT ...'
EXEC(#MyDynamicSql)
A couple of things to watch out for when writing this kind of spaghetti code however:
Re-running the same code (outside of a stored procedure) will necessitate dropping (or at least truncating) the temp tables
Addendum to the above: SQL parser will throw errors at you if you change the structure of the temp tables even if you have a DROP statement (read: you have to drop the tables before running the batch to change their structures)
If your process is a subroutine of one which already declared temp tables by the same name... Make sure to avoid that, I spent hours trying to figure this one out thinking I was going crazy.
Temp tables declared inside dynamic SQL need to be connection- or globally-scoped (2 and 3 #'s, respectively) to persist for the parent to access (documentation)
I want to develop a BizTalk orchestration. Which should insert multiple records into multiple DB tables and retrieve inserted records from multiple DB tables, in single instance of orchestration. For this requirement, I'm able to insert the data in one instance, but seeing difficulty to retrieve the inserted data for that instance, as all the records has unique values for each record. For my situation, I should use stored procedures, to apply some other business logic. So I have 2 different methods by using "Wcf_Custom Adapter composite feature" by calling stored procedures, as stated below.
-> Method1
I have to develop a Stored procedure, which takes LoadDate("2016-05-12 10:11:22.147") as parameter along with inserting values and it will take care of inserting the records for that instance, by keeping the given LoadDate. Then immediately it will call Get stored procedure, which takes the LoadDate("2016-05-12 10:11:22.147") as parameter, then it will retrieve the recently inserted records from DB based on LoadDate value.
I know, Retrieving the data based on a date value from sql server is a bad practice and it will give performance issues too.
-> Method2
I'll design the inserting tables, with bool data type column name "New" and value will be 0 or 1. I'll develop a Insert Stored procedure, which inserts the data by giving the "New" column value as "1". Then immediately it will call Get stored procedure, which will not take no parameters, then it will retrieve the recently inserted records which are having "New" column indicator "1" from DB tables. Once it retrieves the data, then it will update "New" column value to "0".
I prefer this method2. But, do we have better option?
As #johns-305 mentioned in his comment. You shall use table value param in your sp. and assembly all your data in orchestration then make a call to this sp.
A sample sp may like below:
CREATE TYPE [dbo].[SampleDataTable_Type] AS TABLE(
[ID] [int] NOT NULL,
[Name] [varchar](50) NOT NULL,
PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (IGNORE_DUP_KEY = OFF)
)
GO
CREATE PROCEDURE [dbo].[sp_InsertSampleTableData]
(
#LoadDate DATETIME,
#data [SampleDataTable_Type] READONLY
)
AS
BEGIN
SET NOCOUNT ON
INSERT INTO your_table(id, name,)
SELECT id, name FROM #data;
--Do whatever you want
SET NOCOUNT OFF
END
GO
I think your stored procedure may look like this:
create procedure myProc
#a int, #b varchar(100)
as
insert myTable(a,b,c)
OUTPUT inserted.* --this line
select a,b,c
from somewhere
where a=#a and b=#b
I have a SQL Server Database Project in which I've made several changes to the schema where I've changed column data types from NUMERIC (18,0) to INT. We're trying to normalize the data type used for Primary Keys, it's a currently 50/50 mix.
When I generate the Publish script, some of the tables are recreated in the script:
CREATE TABLE [dbo].[tmp_XYZ]
INSERT TABLE [dbo].[tmp_XYZ] SELECT ... FROM [dbo].[XYZ]
DROP TABLE [dbo].[XYZ]
sp_rename N'[dbo].[tmp_XYZ]', N'XYZ';
but other tables are just updated via ALTER statements
ALTER TABLE [dbo].[ABC] ALTER COLUMN [AbcID] INT NULL;
Is there some rule that dictates when a table will be recreated, and when it's just altered in place ?
Probably the best way is to Right Click on your object name and choose script as ...
Then you have options to create or alter
If you couldn't find Alter ,you can go to design view, right click and choose Generate Change Script ... to find the alter statement.
This is just an easy problem. It's the same problem as changing a table in the table designer. I think you've changed a column inside your table design which needs to drop and recreate the table to let the column order in the same position.
Here is a short example. Take this table design as given:
CREATE TABLE dbo.Test(
id int identity(1,1),
firstname nvarchar(100),
name nvarchar(100),
street nvarchar(100)
)
This will create the columns in a specified order. You can see this order here:
SELECT name, column_id
FROM sys.columns
WHERE object_id = OBJECT_ID(N'dbo.Test')
You'll see something like that:
column_name column_id
id 1
firstname 2
name 3
street 4
If you change the the column name via designer or in your case in the data project, this will cause SQL Server to obtain this order upright.
In this case you try to change the column name to lastname. This will enforce SQL Management Studio and other programs like that to keep the column_id upright. This can only be done, if the table is completely recreated with the right columnorder. SQL Server create a temporary table stub, insert everything into it, drop the old table and rename the temporary table to the old original name. Just as in your code above.
After that you'll see something like that:
column_name column_id
id 1
firstname 2
lastname 3
street 4
If you would simply rename the last column or do it manually, everything would be fine. Manually would be much more efficient, as there isn't the need to move ALL data to a new table. The manual way would be this:
-- Create the new column
ALTER TABLE dbo.Test ADD lastname nvarchar(100)
GO
-- Populate the new column using the old one
UPDATE dbo.Test
SET lastname = name
GO
-- Drop the old column afterwards
ALTER TABLE dbo.Test DROP COLUMN name
This behavior will result in the following result:
column_name column_id
id 1
firstname 2
street 4
lastname 5
The last one will be much more efficient, as already stated.
Hopefully this will answer your question, even if the answer comes lately.
When using temp tables in SQL Server stored procs, is the preferred practice to;
1) Create the temp table, populate it, use it then drop it
CREATE TABLE #MyTable ( ... )
-- Do stuff
DROP TABLE #MyTable
2) Check if it exists, drop it if it does, then create and use it
IF object_id('tempdb..#MyTable') IS NOT NULL
DROP TABLE #MyTable
CREATE TABLE #MyTable ( ... )
3) Create it and let SQL Server clean it up when it goes out of scope
CREATE TABLE #MyTable ( ... )
-- Do Stuff
I read in this answer and its associated comments, that this can be useful in situations where the temp table is reused that SQL Server will truncate the table but keep the structure to save time.
My stored proc is likely to be called pretty frequently, but it only contains a few columns, so I don't know how advantageous this really is in my situation.
You could test and see if one method outperforms another in your scenario. I've heard about this reuse benefit but I haven't performed any extensive tests myself. (My gut instinct is to explicitly drop any #temp objects I've created.)
In a single stored procedure you should never have to check if the table exists - unless it is also possible that the procedure is being called from another procedure that might have created a table with the same name. This is why it is good practice to name #temp tables meaningfully instead of using #t, #x, #y etc.
I follow this approach:
IF object_id('tempdb..#MyTable') IS NOT NULL
DROP TABLE #MyTable
CREATE TABLE #MyTable ( ... )
// Do Stuff
IF object_id('tempdb..#MyTable') IS NOT NULL
DROP TABLE #MyTable
Reason: In case if some error occurs in sproc, and created temp table is not dropped and when the same sproc is called with check for existence, it will raise error that table cannot be created, and will never get successfully executed unless the table is dropped. So always perform check for the existence of and object before creating it.
When using temp tables my preferred practice is actually a combination of 1 and 2.
IF object_id('tempdb..#MyTable') IS NOT NULL
DROP TABLE #MyTable
CREATE TABLE #MyTable ( ... )
// Do Stuff
IF object_id('tempdb..#MyTable') IS NOT NULL
DROP TABLE #MyTable