SQL Azure doesn't support 'select into' - Is there another way? - sql

I have a very complicated table I'd like to take a temporary backup of whilst I make some changes. Normally, I'd just do the following:
SELECT *
INTO temp_User
FROM dbo.[User] AS u
Unfortunately I'm using Azure, and it appears this isn't supported:
Msg 40510, Level 16, State 1, Line 2 Statement 'SELECT INTO' is not
supported in this version of SQL Server.
Is there a way to re-create this feature into a function, potentially? I could do this by scripting the table, creating it and then inserting data using a select statement but given how frequently I use Azure, and how many databases I need to work on in this area this is very unwieldy.

Azure requires a clustered index on all tables, therefore SELECT INTO is not supported.
You'll have to:
CREATE TABLE temp_User () --fill in table structure
INSERT INTO temp_User
SELECT *
FROM dbo.[User]
To script table easily you can write your own or use one of the answers to this question:
Script CREATE Table SQL Server
Update: As Jordan B pointed out, V12 will include support for heaps (no clustered index requirement) which means SELECT INTO will work. At the moment V12 Preview is available, Microsoft of course only recommends upgrading with test databases.

The new Azure DB Update preview has this problem resolved:
The V12 preview enables you to create a table that has no clustered
index. This feature is especially helpful for its support of the T-SQL
SELECT...INTO statement which creates a table from a query result.
http://azure.microsoft.com/en-us/documentation/articles/sql-database-preview-whats-new/

Unfortunately it cant be done. Here is how I worked around it:
Open SQL Server Management Studio
Right click on the table
Select Script as ... Create Table
Edit the generated script to change the table name to what you specified in your query
Execute your query

INSERT INTO temp_User
SELECT * FROM dbo.[User]
You can try the above. It's basically a select that is applied to an insert statement
http://blog.sqlauthority.com/2011/08/10/sql-server-use-insert-into-select-instead-of-cursor/

Lets assume you have a table with Id, Column1 and Column2. Then this could be your solution
CREATE TABLE YourTableName_TMP ....
GO
SET IDENTITY_INSERT YourTableName_TMP ON
GO
INSERT INTO YourTableName_TMP
([Id] ,[Column1] ,[Column2])
SELECT [Id] ,[Column1] ,[Column2]
FROM
(
SELECT *
FROM
(
SELECT [Id] ,[Column1] ,[Column2] ROW_NUMBER() OVER(ORDER BY ID DESC) AS RowNum
FROM YourTableName
)
WHERE RowNum BETWEEN 0 AND 500000
)
GO
SET IDENTITY_INSERT YourTableName_TMP OFF
GO
First you create a temporary table and then you insert rows windowed. It's a mess, I know. My experiences are, that executing this using SQL Server Management Studio from a client makes approximately 200.000 rows a minute.

As wrote above - you need to rewrite your query from using select into to create table like
It is my sample. Was :
select emrID, displayName --select into
into #tTable
from emrs
declare #emrid int
declare #counter int = 1
declare #displayName nvarchar(max)
while exists (select * from #tTable)
begin
-- some business logic
select top 1 #displayName = displayname
from #tTable
group by displayname
update emrs set groupId = #counter where #displayName = displayname
delete #tTable
where #displayName = displayname
set #counter = #counter + 1
end
drop table #tTable
Modified :
CREATE TABLE #tTable ([displayName] nvarchar(max)) --create table
INSERT INTO #tTable -- insert to next select :
select displayName
from emrs
declare #emrid int
declare #counter int = 1
declare #displayName nvarchar(max)
while exists (select * from #tTable)
begin
-- some business logic
select top 1 #displayName = t.displayName
from #tTable as t
group by t.displayname
update emrs set groupId = #counter where #displayName = displayname
delete #tTable
where #displayName = displayname
set #counter = #counter + 1
end
drop table #tTable
Do not forget to drop your temp table.
Also, you can find more simple example with description here :
http://www.dnnsoftware.com/wiki/statement-select-into-is-not-supported-in-this-version-of-sql-server

Related

Conditional SQL Insert

I have to write a. insert statement that looks at a table and inserts a record if the conditions are met. This is a one time thing so not overly concerned about it being efficient.
the table contains a work breakdown structure for a project ( each project having, a project level(wbs1), a phase level(wbs2) and a task level (wbs3)
that table looks like this
Wbs1 wbs2 wbs3 name
262 ProjectA
262 01 Data Analsys
262 01 01 Data cleansing
262 01 02 Data Transforming
I need to insert a phase(WBS2) to each project(WBS1) with an insert statement, for example adding a wbs2 "02" to each project(wbs1).
writing the insert statment is no problem and I select the data from the project level since most of it is redundant so no issue there, im just not sure how to have it loop through and add the phase to each project, since there are multiple rows with the same project(wbs1) number
insert statement sample
Insert into dbo.pr ([WBS1],[WBS2],[WBS3],[Name])
(Select [WBS1],'999',[WBS3],'In-House Expenses'
from dbo.pr where wbs1 = #ProjectID
and wbs2 ='')
How do i run this statement to inserta row every project?(wbs1)
hopefully this makes sense.
You can use a temporary table with an added RowNumber field and then a WHILE loop to handle the looping over each row. You can then run an IF EXISTS as a criteria check before running the stored procedure. See below for example
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
DECLARE #ProjectId NVARCHAR(50) = '262'
CREATE TABLE #Temp (RowNumber INT, wbs1 NVARCHAR(255), wbs2 NVARCHAR(255), wbs3 NVARCHAR(255), name NVARCHAR(255))
INSERT INTO #Temp
SELECT ROW_NUMBER() OVER (ORDER BY wbs1, wbs2, wbs3, name)
,pr.*
FROM pr
select *
from #temp
-- Create loop variables to handle incremeting
DECLARE #Counter INT = 1;
DECLARE #MaxLoop INT = (SELECT COUNT(wbs1) FROM #temp)
WHILE #Counter <= #MaxLoop
BEGIN
-- Use if Exists to check the current looped meets whatever critiera you have
IF EXISTS (SELECT 'true'
FROM #Temp
WHERE RowNumber = #Counter
AND wbs1 = #ProjectId
AND wbs2 = ''
)
BEGIN
Insert into pr (wbs1,wbs2,wbs3,name)
(Select [WBS1],'999',[WBS3],'In-House Expenses'
from #temp where RowNumber = #Counter)
END
-- Remember to increment the counter
SET #Counter = #Counter + 1;
END
SELECT *
FROM pr
drop table #temp

Using variables in stored procedures

I am using Microsoft SQL server and writing a stored procedure that contains many select statements. Here I want two variables that hold results of two select statements, and I want to add those two variables to get final result. Please help me doing this (syntax and example).
Below is the syntax for SQL Server:
DECLARE #UserEmail Varchar(250)
DECLARE #LoginID INT
SET #UserEmail = 'a#b.org'
select #LoginID = LoginID from Login L
Where L.UserEmail = #UserEmail
You should clarify which DB you are using. In MS SQL Server, you can use temporary table variable like this:
BEGIN
SELECT product_id,product_name INTO #temp1 FROM products;
SELECT product_id,product_name INTO #temp2 FROM products;
SELECT * FROM #temp1
UNION
SELECT * FROM #temp2;
END
There are several types of temporary table variable in MS SQL Server.I've used one of them.To know more about this, just search "MS SQL Server Temporary tables" in web.
EDIT:
Here is another example with another type of temporary table variable in MS SQL Server.
DECLARE #temp1 TABLE (product_id INT,product_name VARCHAR(100));
DECLARE #temp2 TABLE (product_id INT,product_name VARCHAR(100));
INSERT INTO #temp1 SELECT product_id,product_name FROM products WHERE cat_id=1;
INSERT INTO #temp2 SELECT product_id,product_name FROM products WHERE cat_id=2;
SELECT product_id,product_name
FROM #temp1
UNION
SELECT product_id,product_name
FROM #temp2;

create temp table of trigger data

I am trying to create an audit trigger without having to specifiy the column list more than once.
To this end, I want to product a temporary table of the content of the INSERTED or DELETED data in the trigger, then process that into an audit table.
If I use this:
IF #ChangeType = 'D'
SELECT * INTO #tmp FROM DELETED
ELSE
SELECT * INTO #tmp FROM INSERTED
Then I get a compilation error at the 2nd SELECT * INTO that the table #tmp already exists.
If I try and work around this using dynamic SQL:
SET #Sql = 'SELECT * INTO #tmp FROM '
IF #ChangeType = 'D'
SET #Sql = #Sq + 'DELETED'
ELSE
SET #Sql = #Sql + 'INSERTED'
EXEC (#Sql)
Then I get an error that the DELETED and INSERTED tables do not exist.
How can I get the INSERTED and DELETED tables in a trigger into a temporary or other in-memory table?
Try to create the temporary table outside the if, like:
SELECT TOP 0 * INTO #tmp FROM DELETED
IF #ChangeType = 'D'
INSERT INTO #tmp SELECT * FROM DELETED
ELSE
INSERT INTO #tmp SELECT * FROM INSERTED
This is a known problem due to the resolve-on-parse of the temp table object. With two SELECT - INTO statements in the same scope, SQL Server throws the towel.
SELECT * INTO #tmp FROM DELETED WHERE 1=0
IF #ChangeType = 'D'
INSERT #tmp SELECT * FROM DELETED
ELSE
INSERT #tmp SELECT * FROM INSERTED
I'd be interested as to why you need to copy the data into another table in the first place. But, that's off-topic...
Temporary table (#temp) are notionally stored on disc, and Table Variables (#temp) are notionally only in memory and may be more optimal for small tasks. (Assumes writes to the table will normally only affect small numbers of rows.)
Temporary tables, however, can be created using the SELECT INTO trick, avoiding the need to know the table definition in advance.
If you do know the table definition in advance, however, can't you simply use something such as the following?
DECLARE #temp TABLE (id AS INT, val as INT)
IF #ChangeType = 'D'
INSERT INTO #temp SELECT * FROM DELETED
ELSE
INSERT INTO #temp SELECT * FROM INSERTED
Personally, I'd even avoid using * if possible. Your subsequent queries will only use specific fields, so I'd only copy the fields I was using. This has the added benefit that if fields are added to the table, the code doesn't break...
DECLARE #temp TABLE (id AS INT, val as INT)
IF #ChangeType = 'D'
INSERT INTO #temp SELECT id, val FROM DELETED
ELSE
INSERT INTO #temp SELECT id, val FROM INSERTED
In my mind, the advantage of specifying the fields (which is what you wish to avoid), is that you can ensure that you always only copy what you need.

delete old records and keep 10 latest in sql compact

i'm using a sql compact database(sdf) in MS SQL 2008.
in the table 'Job', each id has multiple jobs.
there is a system regularly add jobs into the table.
I would like to keep the 10 latest records for each id order by their 'datecompleted'
and delete the rest of the records
how can i construct my query? failed in using #temp table and cursor
Well it is fast approaching Christmas, so here is my gift to you, an example script that demonstrates what I believe it is that you are trying to achieve. No I don't have a big white fluffy beard ;-)
CREATE TABLE TestJobSetTable
(
ID INT IDENTITY(1,1) not null PRIMARY KEY,
JobID INT not null,
DateCompleted DATETIME not null
);
--Create some test data
DECLARE #iX INT;
SET #iX = 0
WHILE(#iX < 15)
BEGIN
INSERT INTO TestJobSetTable(JobID,DateCompleted) VALUES(1,getDate())
INSERT INTO TestJobSetTable(JobID,DateCompleted) VALUES(34,getDate())
SET #iX = #iX + 1;
WAITFOR DELAY '00:00:0:01'
END
--Create some more test data, for when there may be job groups with less than 10 records.
SET #iX = 0
WHILE(#iX < 6)
BEGIN
INSERT INTO TestJobSetTable(JobID,DateCompleted) VALUES(23,getDate())
SET #iX = #iX + 1;
WAITFOR DELAY '00:00:0:01'
END
--Review the data set
SELECT * FROM TestJobSetTable;
--Apply the deletion to the remainder of the data set.
WITH TenMostRecentCompletedJobs AS
(
SELECT ID, JobID, DateCompleted
FROM TestJobSetTable A
WHERE ID in
(
SELECT TOP 10 ID
FROM TestJobSetTable
WHERE JobID = A.JobID
ORDER BY DateCompleted DESC
)
)
--SELECT * FROM TenMostRecentCompletedJobs ORDER BY JobID,DateCompleted desc;
DELETE FROM TestJobSetTable
WHERE ID NOT IN(SELECT ID FROM TenMostRecentCompletedJobs)
--Now only data of interest remains
SELECT * FROM TestJobSetTable
DROP TABLE TestJobSetTable;
How about something like:
DELETE FROM
Job
WHERE NOT
id IN (
SELECT TOP 10 id
FROM Job
ORDER BY datecompleted)
This is assuming you're using 3.5 because nested SELECT is only available in this version or higher.
I did not read the question correctly. I suspect something more along the lines of a CTE will solve the problem, using similar logic. You want to build a query that identifies the records you want to keep, as your starting point.
Using CTE on SQL Server Compact 3.5

There is already an object named '#columntable' in the database

I am trying the following query
if exists (select 1 from emp where eid = 6)
begin
if object_id('tempdb..#columntable') is not null
begin
drop table #columntable
end
create table #columntable (oldcolumns varchar(100))
end
else
begin
if object_id('tempdb..#columntable') is not null
begin
drop table #columntable
end
create table #columntable (newcolumns varchar(100))
end
But I am getting the error
Msg 2714, Level 16, State 1, Line 8
There is already an object named '#columntable' in the database.
Can anyone suggest why? The same query works fine if I do not write the else part.
This is a SQL Server parser error unfortunately (confirmed by Microsoft).
#DizGrizz is also right - SELECT .. INTO #SomeTable doesn't work if repeated in IF .. ELSE statements.
IF .. ELSE .. CREATE TABLE #SomeTempTable
In answer to the actual question, creating then altering the table works (you also only have to check and drop once)...
IF OBJECT_ID('tempdb..#MyTempTable') IS NOT NULL
BEGIN
DROP TABLE #MyTempTable
END
CREATE TABLE #MyTempTable (DummyColumn BIT)
IF EXISTS (SELECT 1 FROM EMP WHERE EID = 6)
BEGIN
ALTER TABLE #MyTempTable
ADD MyColumnType1 VARCHAR(100)
ALTER TABLE #MyTempTable
DROP COLUMN DummyColumn
END
ELSE
BEGIN
ALTER TABLE #MyTempTable
ADD MyColumnType2 VARCHAR(100)
ALTER TABLE #MyTempTable
DROP COLUMN DummyColumn
END
IF .. ELSE .. SELECT INTO #SomeTempTable
The issue I had however was the same as #DizGrizz: IF .. ELSE combined with SELECT .. INTO #SomeTable fails. As a workaround it's possible to select the top 0 rows (i.e. none) to create the table with the correct column types. (This insulates the script from column type changes and also avoids the pain of declaring every type.) INSERT INTO can then be used, provided IDENTITY_INSERT is set to ON to prevent errors:
IF OBJECT_ID('tempdb..#MyTempTable') IS NOT NULL
DROP TABLE #MyTempTable
-- This creates the table, but avoids having to declare any column types or sizes
SELECT TOP 0 KeyNm
INTO #MyTempTable
FROM dbo.MyDataTable2
-- Required to prevent IDENTITY_INSERT error
SET IDENTITY_INSERT #MyTempTable ON
IF #something = 1
BEGIN
-- Insert the actual rows required into the (currently empty) temp table
INSERT INTO #MyTempTable (KeyNm)
SELECT KeyNm
FROM dbo.MyDataTable2
WHERE CatNum = 2
END
ELSE
BEGIN
-- Insert the actual rows required into the temp table
INSERT INTO #MyTempTable (KeyNm)
SELECT KeyNm
FROM dbo.MyDataTable2
WHERE CatNum = 8
END
SET IDENTITY_INSERT #MyTempTable OFF
Temp tables are not dropped automatically at the end of a query, only when the current connection to the DB is dropped or you explicitly delete them with DROP TABLE #columntable
Either test for the existence of the table at the start of the query or alwayas delete it at the end (preferably both)
EDIT: As Matrin said in his comment, this is actually a parse error. You get the same error if you only parse the SQL as when you execute it.
To test that out I split up your query and tried:
if exists (select 1 from emp where id = 6)
create table #columntable (newcolumns varchar(100))
GO
if not exists (select 1 from emp where id = 6)
create table #columntable (oldcolumns varchar(100))
GO
The parser is happy with that. Interestingly if you change to using non-temp tables the original query parses fine (I realise the problems that would create, I was just interested to find out why the query would not parse).
This also occurs if you create the tables with SELECT INTO...as in
IF OBJECT_ID('tempdb..#MyTempTable', 'U') IS NOT NULL
DROP TABLE #MyTempTable
SELECT TOP 1 #MyVariable = ScaleValue
FROM MyDataTable1
WHERE ProductWeight > 1000
IF #MyVariable = 1
BEGIN
SELECT KeyNm
INTO #MyTempTable
FROM dbo.MyDataTable2
WHERE CatNum = 2
END
ELSE
BEGIN
SELECT KeyNm
INTO #MyTempTable
FROM dbo.MyDataTable2
WHERE CatNum = 8
END
The parser should not even attempt to detect this because, in many cases, it would be impossible for the parser to determine if the table would already exist. The code above is a perfect example...there would be no way for the parser to determine the value of #MyVariable.
I hope that someone has informed MS of this bug (I don't have their ear).
You can check if it exists by doing:
IF OBJECT_ID('tempdb..#columntable') IS NOT NULL
BEGIN
DROP TABLE #columntable
PRINT 'Dropped table...'
END
Use global temp tables and wrap the select in exec.
Example:
Fail
declare #a int = 1
if object_id('tempdb..##temp') is not null drop table ##temp
if(#a = 1) select * into ##temp from table_1
else if(#a = 2) select * into ##temp from table_2
Win
declare #a int = 1
if object_id('tempdb..##temp') is not null drop table ##temp
if(#a = 1) exec('select * into ##temp from table_1')
else if(#a = 2) exec('select * into ##temp from table_2')
This will fool the buggy parser that is trying to be smarter than it is.
And Microsoft - please fix this.
The error is wrong, remove the if clause and it runs through fine. Thus the problem is in the exists:
if object_id('tempdb..#columntable') is not null
begin
drop table #columntable
end
create table #columntable (oldcolumns varchar(100))
Well I got the answer...
As Martin said this is a parse/compile issue. So I Tried changing my script as below
if exists (select 1 from emp where eid = 6)
begin
if object_id('tempdb..#columntable') is not null
begin
drop table #columntable
end
create table #columntable (oldcolumns varchar(100))
end
go
if exists (select 1 from emp where eid = 1)
begin
if object_id('tempdb..#columntable') is not null
begin
drop table #columntable
end
create table #columntable (newcolumns varchar(100))
end
And this worked for me.
I have been experiencing this issue. My query consisted of several joined SELECT statements in the form of:
DROP TABLE IF EXISTS ##TempTableName
SELECT statement ...
So, every time I tried to alter SQL code I would get the above error. I have changed all my temp tables from ##global to #local and now I am able to alter my SQL as many times as needed. So the example above would become:
DROP TABLE IF EXISTS #TempTableName
SELECT statement ...