Temp tables: CREATE vs. SELECT INTO - sql-server

I've searched and found this article about temporary tables in SQL Server because I've met a line in one of our stored procedures saying:
SELECT Value SomeId INTO #SomeTable FROM [dbo].[SplitIds](#SomeIds, ';')
I know that #SomeTable is stored in tempdb as a temporary table. However, I don't understand why we don't have to use CREATE TABLE #SomeTable first as it is written in the mentioned article. Our code is working fine, I just don't get why it is enough to use SELECT ... INTO #SomeTable. What would be the consequence when I add CREATE TABLE #SomeTable at the beginning? Would we get any differences in performance? Would the table be stored at another location?

Select ... into [table] uses the properties of the dataset generated from the Select statement to create a temporary table and subsequently fill the table.
The alternative to using Select ... into [table] is to use a Create Table statement followed by an Insert Into statement. Explicitly creating the table offers more control and precision.
Using a Select ... into [Table] may seem like a no-brainer, but there are situations where Select ... into [Table] can be problematic.
For instance, when you are going to create a temporary table and insert additional rows at a later time, using the Select ... into [Table] syntax can cause problems, especially with string-based and nullable fields.
As an example of the limitations of the Select ... into [table], the script below creates a temporary table with two fields, First_Name and Last_Name. Next, an Insert statement attempts to add another record to the temporary table, but fails as the values would be truncated.
Select 'Bob' as First_Name
, 'Smith' as Last_Name
Into #tempTable;
Insert into #tempTable (First_Name, Last_Name)
Select 'Christopher' as First_Name
, 'Brown' as Last_Name;
The script fails because the Select ... into [table] statement creates a table equivalent to the following script:
Create Table #tempTable (
First_Name varchar(3) Not Null
Last_Name varchar(5) Not Null
);

Related

T-SQL OUTPUT clause to update a temp table

I have a utility script that is used to insert data into tables in my database. The script has a number of temp table in it that stores the new data to be inserted and a lot of it is related.
So, for example I have tables like so
DECLARE #Table1 TABLE
(
Table1ID INT
Table1Description VARCHAR(50)
Table1Code VARCHAR(5)
)
DECLARE #Table2 TABLE
(
Table2ID INT
Table2Description VARCHAR(50)
Table2Code VARCHAR(5)
)
DECLARE #Relationships TABLE
(
Table1Code VARCHAR(5)
Table2Code VARCHAR(5)
)
So the script populates the data in #Table1 and #Table2, but doesn't populate the ID fields. Once the data has been MERGEd into the database tables, I update the Table1ID and Table2ID fields in a separate statement as they are auto incrementing fields. Then when I use the #Relationships table to populate the database table, I can join to #Table1 and #Table2 to get the actual ID values.
I'm updating the script and I'm wondering if I can MERGE the data from #Table1/#Table2 into the database and update the ID fields in the temp table as part of the MERGE statement using the OUTPUT clause all in one statement?
I think the answer is no as I can't find anything mentioning updating an existing table with the OUTPUT clause, only inserting into a table.
I am still able to do what I need to do, so I'm not after alternatives. I just wondering if it is possible using the OUTPUT Clause
Thanks in advance

How can we completely replace the contents of a table with another table in a SQL Server database?

How can we completely replace the contents of a table with another table in a SQL Server database?
Like Truncate. We want to take all the data in one and put the data in the other one into it. How can we do? How do we do it with a script? By doing this automatically.
It happens in Oracle, does it happen in SQL Server? Thanks.
Just make simple insert into statement like this :
Also you can create procedure that will makes like just my simple example
DECLARE #employee1 TABLE(
Emp VARCHAR(100),
DOB datetime
)
INSERT INTO #employee1 SELECT 'ABC','1991-03-01'
INSERT INTO #employee1 SELECT 'XYZ','1992-12-01'
INSERT INTO #employee1 SELECT 'AJM','1992-08-20'
INSERT INTO #employee1 SELECT 'RNM','1991-07-10'
DECLARE #employee2 TABLE(
Emp VARCHAR(100),
DOB datetime
)
INSERT INTO #employee2
select * from #employee1
select '#employee1',* from #employee1
select '#employee2',* from #employee2
SQLFiddleDemo
If i am correct schema is same for both tables. In this case there are many way you can achieve this automatically,
Option 1: Call Stored Procedure from any web page or window service or any api.
Option 2: Create triggers.
In both way you can use following script to copy data from one table to other:
Truncate table <table1>
GO
INSERT <Table1> (column1, column2)
SELECT Column1, column2 FROM <Table2>
GO

SQL Server - Create temp table if doesn't exist

In my SQL Server 2012 environment, I've created a series of stored procedures that pass pre-existing temporary tables among themselves (I have tried different architectures here, but wasn't able to bypass this due to the nature of the requirements / procedures).
What I'm trying to do is to, within a stored procedure check if a temporary table has already been created and, if not, to create it.
My current SQL looks as follows:
IF OBJECT_ID('tempdb..#MyTable') IS NULL
CREATE TABLE #MyTable
(
Col1 INT,
Col2 VARCHAR(10)
...
);
But when I try and run it when the table already exists, I get the error message
There is already an object named '#MyTable' in the database
So it seems it doesn't simply ignore those lines within the If statement.
Is there a way to accomplish this - create a temp table if it doesn't already exist, otherwise, use the one already in memory?
Thanks!
UPDATE:
For whatever reason, following #RaduGheorghiu's suggestion from the comments, I found out that the system creates a temporary table with a name along the lines of dbo.#MyTable________________________________________________0000000001B1
Is that why I can't find it? Is there any way to change that? This is new to me....
Following the link here, http://weblogs.sqlteam.com/mladenp/archive/2008/08/21/SQL-Server-2005-temporary-tables-bug-feature-or-expected-behavior.aspx
It seems as though you need to use the GO statement.
You meant to use IS NOT NULL i think... this is commonly used to clear temp tables so you don't get the error you mentioned in your OP.
IF OBJECT_ID('tempdb..#MyTable') IS NOT NULL DROP TABLE #MyTable
CREATE TABLE #MyTable
(
Col1 INT,
Col2 VARCHAR(10)
);
The big difference is the DROP TABLE statement after you do your logical check. Also, creating your table without filling data doesn't make it NULL
DROP TABLE #MyTable
CREATE TABLE #MyTable
(
Col1 INT,
Col2 VARCHAR(10)
);
IF OBJECT_ID('tempdb..#MyTable') IS NOT NULL
SELECT 1
Try wrapping your actions in a begin...end block:
if object_id('tempdb..#MyTable') is null
begin
create table #MyTable (
Col1 int
, Col2 varchar(10)
);
end
This seems odd, but it works when I try it
IF(OBJECT_ID('tempdb..#Test') IS NULL) --check if it exists
BEGIN
IF(1 = 0)--this will never actually run, but it tricks the parser into allowing the CREATE to run
DROP TABLE #Test;
PRINT 'Create table';
CREATE TABLE #Test
(
ID INT NOT NULL PRIMARY KEY
);
END
IF(NOT EXISTS(SELECT 1 FROM #Test))
INSERT INTO #Test(ID)
VALUES(1);
SELECT *
FROM #Test;
--Try dropping the table and test again
--DROP TABLE #Test;

SQL Server delete trigger returns select statement, how to capture that data?

On a table, there's a delete trigger that performs some operations and then at the end, executes a select statement, so when you do something like...
delete from mytable where id=1
it returns a recordset.
Is there a way to save the results of that recordset into a temp table or something? I tried something like this:
declare #temptable table (returnvalue int);
insert into #temptable (returnvalue)
delete from mytable where id=1;
But apparently that syntax doesn't work.
Well,
I can not imagine a situation that you need to return the recordset of the line you will delete using a trigger returning a recordset. But I am not here to judge your requests.
Well, you can use the OUTPUT to show the row data that will be excluded and enter this data into a temporary table. Follow the example below.
However you should know that: SQL Server does not guarantee the order in Which rows are processed and returned by DML statements using the OUTPUT clause. It is up to the application to include an WHERE clause Appropriate que can guarantee the Desired semantics, or Understand que When multiple rows may qualify for the DML operation, there is guaranteed in order. The Following example uses the subquery and you assume uniqueness is a characteristic of the column in order to DatabaseLogID in Place the Desired ordering semantics. See the link.
Example:
CREATE TABLE Person
(
PersonID int,
LastName varchar(255),
FirstName varchar(255)
);
GO
--DECLARE #MyTablePerson TABLE
--(
-- PersonID int,
-- LastName varchar(255),
-- FirstName varchar(255)
--);
--GO
--CREATE TRIGGER TRG_DLT_Person
--ON Person
--INSTEAD OF DELETE
--AS
--BEGIN
-- Some code you want to do before delete
-- DELETE Person
-- FROM DELETED D
--END
--GO
insert into Person
(PersonID,
LastName,
FirstName)
values
(1,
'Kilmister',
'Lemmy');
GO
insert into Person
(PersonID,
LastName,
FirstName)
values
(2,
'Gilmour',
'David');
GO
insert into Person
(PersonID,
LastName,
FirstName)
values
(3,
'Rose',
'Axl');
GO
insert into Person
(PersonID,
LastName,
FirstName)
values
(4,
'Bullock',
'Sandra');
GO
--
select * from Person;
GO
delete from Person
--output deleted.* INTO #MyTablePerson
output deleted.*
WHERE PersonID = 4 OR PersonID = 2;
GO
select * from Person;
GO
select * from #MyTablePerson;
GO
I put the example I'm showing in a this environment, but in this environment believe that are not supported for temporary tables.
SQL Fiddle
Regardless of this being a bad practice due to it being difficult for anyone interacting with the table to know that it will happen and deal with it when it does, and regardless of it being possible to capture, one pretty solid reason to not return result sets from a trigger is that doing so will be disallowed as of one of the next versions of SQL Server, so you would have to re-code the functionality anyway. The MSDN page for the disallow results from triggers Server Configuration Option states:
Important
This feature will be removed in the next version of Microsoft SQL Server. Do not use this feature in new development work, and modify applications that currently use this feature as soon as possible. We recommend that you set this value to 1.
If you are merely returning something like SELECT IdField FROM deleted; from the trigger, then you should (well, really need to) use the OUTPUT clause instead.
That being said, doing the following will do what you want:
CREATE TABLE #TempResults
(
ReturnValue INT
);
INSERT INTO #TempResults (ReturnValue)
EXEC('DELETE FROM mytable WHERE id = 1;');
You can test with the following:
SET NOCOUNT ON;
IF (OBJECT_ID('dbo.DeleteTriggerWithResults') IS NOT NULL)
BEGIN
DROP TABLE dbo.DeleteTriggerWithResults;
END;
CREATE TABLE dbo.DeleteTriggerWithResults
(
Col1 INT NOT NULL IDENTITY(1, 1),
Col2 DATETIME DEFAULT (GETDATE())
);
GO
CREATE TRIGGER dbo.tr_DeleteTriggerWithResults_d
ON dbo.DeleteTriggerWithResults
AFTER DELETE
AS
BEGIN
SELECT Col1
FROM deleted;
END;
GO
INSERT INTO dbo.DeleteTriggerWithResults DEFAULT VALUES;
GO 30
SELECT * FROM dbo.DeleteTriggerWithResults;
And then run the test:
DECLARE #TempResults TABLE (Col1 INT);
INSERT INTO #TempResults (Col1)
EXEC('
DELETE TOP (10)
FROM dbo.DeleteTriggerWithResults;
');
SELECT * FROM #TempResults;
Returns:
Col1
-------
10
9
8
7
6
5
4
3
2
1

Retrieve original and new identities mapping from SELECT INSERT statement using OUTPUT clause

I have a table with two columns:
CREATE TABLE MyTable(
Id int IDENTITY(1,1) NOT NULL,
Name nvarchar(100) NOT NULL);
I want to duplicate the data using SELECT INSERT statement:
INSERT INTO MyTable (Name)
SELECT Name FROM MyTable
and here is the trickey part - I want to retrieve a mapping table between the original identity and the new identity:
DECLARE #idsMap TABLE (OriginalId int, NewId int)
I know I suppose to use the OUTPUT clause, but for some reason it doesn't work:
INSERT INTO MyTable (Name)
OUTPUT t.Id, INSERTED.Id INTO #idsMap (OriginalId, NewId)
SELECT Name FROM MyTable t
-- Returns error The multi-part identifier "t.Id" could not be bound.
Related questions:
can SQL insert using select return multiple identities?
Possible to insert with a Table Parameter, and also retrieve identity values?
It can be achieved using MERGE INTO and OUTPUT:
MERGE INTO MyTable AS tgt
USING MyTable AS src ON 1=0 --Never match
WHEN NOT MATCHED THEN
INSERT (Name)
VALUES (src.Name)
OUTPUT
src.Id,
inserted.Id
INTO #idsMap;
How about just adding a new column to MyTable? You can keep it around as long as you need to analysis or whatever. I have to say it seems a bit off to me to create a copy of the table but that is up to you to decide.
Something like this might work for you.
alter table MyTable
add OldID int null;
INSERT INTO MyTable (Name, OldID)
SELECT Name , Id
FROM MyTable t
select * from MyTable

Resources