mssql multiple queries insert queries and results - sql-server

In php I'm executing multiple queries at once, the queries are wrote in one big variable.
These are insert queries and I have to retrieve each autoincrement "id" of the records created in the db. How to do it ?
The results set retrieved don't seems to keep each single result but just one.

I think that you are talking about an OUTPUT clause from the insert statement.
http://msdn.microsoft.com/en-us/library/ms177564.aspx
CREATE TABLE #t (id int identity (1, 1), f1 nvarchar(20 ) ) --the table that has the identities
CREATE TABLE #ids ( id int ) --the table to store the inserts into table #t
INSERT INTO #t ( f1 ) OUTPUT INSERTED.id INTO #ids SELECT N'AAAA'
INSERT INTO #t ( f1 ) OUTPUT INSERTED.id INTO #ids SELECT N'BBBB'
INSERT INTO #t ( f1 ) OUTPUT INSERTED.id INTO #ids SELECT N'CCCC'
SELECT * FROM #t
SELECT * FROM #ids
Another way is to use ##IDENTITY or SCOPE_IDENTITY() SQL Authority link discussing/comparing them
CREATE TABLE #t (id int identity (1, 1), f1 nvarchar(20 ) )
CREATE TABLE #ids ( id int )
INSERT INTO #t ( f1 ) SELECT N'AAAA'
INSERT INTO #ids SELECT ##IDENTITY --OR you can use SELECT SCOPE_IDENTITY()
INSERT INTO #t ( f1 ) SELECT N'BBBB'
INSERT INTO #ids SELECT ##IDENTITY --OR you can use SELECT SCOPE_IDENTITY()
INSERT INTO #t ( f1 ) SELECT N'CCCC'
INSERT INTO #ids SELECT ##IDENTITY --OR you can use SELECT SCOPE_IDENTITY()
SELECT * FROM #t
SELECT * FROM #ids

Related

How to autoincrement the id without identity?

I'm trying do to a bulk insert from another table in sql server. My query is currently like that :
INSERT INTO Table1(Id, Value)
SELECT ??, Value
FROM Table2;
Now, my problem is obviously by what I replace ??. Id is an integer column without an identity property. I would like that for each inserted row, Id take the current max(Id) + 1.
Can I do that directly in my insert command?
If you were using a newer version of SQL Server (2008+) you could try ROW_NUMBER():
DECLARE #BASE INT
SET #BASE = (SELECT IsNull(MAX(ID),0) FROM Table1)
INSERT INTO Table1(Id, Value)
SELECT
#BASE + ROW_NUMBER() OVER (ORDER BY Value) ID,
Value
FROM Table2;
SQL Fiddle
Since you are using SQL Server 2000, you could try like bellow:
DECLARE #BASE INT
SET #BASE = (SELECT IsNull(MAX(ID),0) FROM Table1)
INSERT INTO Table1(Id, Value)
SELECT
#BASE + (SELECT COUNT(*) FROM Table2 AS i2 WHERE i2.Value <= a.Value),
a.Value
FROM Table2 a
But it will only works if Value in Table2 is unique
SQL Fiddle
If Table2 has a primary key (field PK), then you could use:
INSERT INTO Table1(Id, Value)
SELECT
#BASE + (SELECT COUNT(*) FROM Table2 AS i2 WHERE i2.PK <= a.PK),
a.Value
FROM Table2 a
Here is one wicked way.
We create a temp table with identity to generate new ids. This way we avoid the while loop.
DECLARE #CurrentMaxID INT,
#DynamicQuery NVARCHAR(MAX)
--TODO : Acquired table lock here on table1
SELECT #FirstNextID = ISNULL(MAX(Id), 0)
FROM Table1 --WITH(TABLOCK)
CREATE TABLE #TempTableWithID( Table2Id INT,
Table1FuturId INT IDENTITY(1, 1))
INSERT INTO #TempTableWithID(Table2Id)
SELECT Id --Here we use identity to generate table1 futur id
FROM Table2
INSERT INTO Table1(Id, value)
SELECT Temp.Table1FuturId + #FirstNextID,
Table2.Value
FROM Table2
INNER JOIN #TempTableWithID AS Temp ON Table2.Id = Temp.Table2Id
--TODO : release table lock here on table1
DROP TABLE #TempTableWithID
If I'm understanding you correctly, this should work.
CREATE TABLE #tbl1 (ID int, Value float)
CREATE TABLE #tbl2 (ID int, Value float)
INSERT INTO #tbl2 values (4, 2.0)
INSERT INTO #tbl2 values (8, 3.0)
INSERT INTO #tbl2 values (6, 4.0)
INSERT INTO #tbl1 values (1,1.0)
INSERT INTO #tbl1 values (3,3)
INSERT INTO #tbl1 values (9,3)
/*meat and potatoes start*/
INSERT INTO #tbl1(Id, Value)
SELECT (SELECT MAX(ID) FROM #tbl1) + ROW_NUMBER() OVER (ORDER BY Value) ID, Value
FROM #tbl2;
/*meat and potatoes end*/
Select * From #tbl1
drop table #tbl1
drop table #tbl2
Why not IDENT_CURRENT() ?
SELECT IDENT_CURRENT('yourtablename')
It gives you the next ID reference. But this only works if the ID column has IDENTITY turned on.
OR you can try a SEQUENCE and the NEXT VALUE FOR.
i.e.
CREATE TABLE Test.TestTable
(CounterColumn int PRIMARY KEY,
Name nvarchar(25) NOT NULL) ;
GO
INSERT Test.TestTable (CounterColumn,Name)
VALUES (NEXT VALUE FOR Test.CountBy1, 'Syed') ;
GO
SELECT * FROM Test.TestTable;
GO

Delete from source table then insert to target table if not exists

Need to delete all rows from a source table , then insert the deleted rows to target table
ONLY if the deleted row doesn't exists yet in the target table.
Is it possible to issue using a single sql?
Code is the one I tried so far (though with error).
Thank You!
create table #Target (column01 varchar(100)
,employee_number varchar(10)
)
Insert into #Target (column01, employee_number)
values ('2','222')
create table #Srs (column01 varchar(100)
,employee_number varchar(10)
)
Insert into #Srs (column01, employee_number)
values ('1','111')
,('2','222')
,('3','333')
,('4','444')
;with cteTable as (Select column01, employee_number from #Srs)
insert into #Target (column01, employee_number)
select * from (Delete from cteTable output deleted.column01, deleted.employee_number) t
where not exists (select 1
from #Target t1
where t1.employee_number = t.employee_number)
The 2,'222' should not be inserted into #Target on call to ";with cteTable.."
SQL FIDDLE DEMO
Composable DML is quite limited.
You can do this if you change the definition of #Target though
CREATE TABLE #Target
(
column01 VARCHAR(100),
employee_number VARCHAR(10) PRIMARY KEY WITH (IGNORE_DUP_KEY=ON)
)
INSERT INTO #Target
(column01,
employee_number)
VALUES ('2',
'222')
CREATE TABLE #Srs
(
column01 VARCHAR(100),
employee_number VARCHAR(10)
)
INSERT INTO #Srs
(column01,
employee_number)
VALUES ('1', '111'),
('2', '222'),
('3', '333'),
('4', '444');
WITH cteTable
AS (SELECT column01,
employee_number
FROM #Srs)
INSERT INTO #Target
(column01,
employee_number)
SELECT * from (Delete from cteTable output deleted.column01, deleted.employee_number) t
Does it have to be only one statement? If not you can use this.
begin transaction;
insert into Target(column01, employee_number)
select column01, employee_number
from Srs with (updlock, holdlock)
except
select column01, employee_number
from Target;
delete from Srs;
commit transaction;

Inserting batch of rows into two tables in SQL Server 2008

I have a requirement to insert multiple rows into table1 and at the same time insert a row into table2 with a pkID from table1 and a value that comes from a SP parameter.
I created a stored procedure that performs a batch insert with a table valued parameter which contains the rows to be inserted into table1. But I have a problem with inserting the row into table2 with the corresponding Id (identity) from table1, along with parameter value that I have passed.
Is there anyone who implemented this, or what is the good solution for this?
CREATE PROCEDURE [dbo].[oSP_TV_Insert]
#uID int
,#IsActive int
,#Type int -- i need to insert this in table 2
,#dTableGroup table1 READONLY -- this one is a table valued
AS
DECLARE #SQL varchar(2000)
DECLARE #table1Id int
BEGIN
INSERT INTO dbo.table1
(uID
,Name
,Contact
,Address
,City
,State
,Zip
,Phone
,Active)
SELECT
#uID
,Name
,Contact
,Address
,City
,State
,Zip
,Phone
,Active
,#G_Active
FROM #dTableGroup
--the above query will perform batch insert using the records from dTableGroup which is table valued
SET #table1ID = SCOPE_IDENTITY()
-- this below will perform inserting records to table2 with every Id inserted in table1.
Insert into table2(#table1ID , #type)
You need to temporarily store the inserted identity values and then create a second INSERT statement - using the OUTPUT clause.
Something like:
-- declare table variable to hold the ID's that are being inserted
DECLARE #InsertedIDs TABLE (ID INT)
-- insert values into table1 - output the inserted ID's into #InsertedIDs
INSERT INTO dbo.table1(ID, Name, Contact, Address, City, State, Zip, Phone, Active)
OUTPUT INSERTED.ID INTO #InsertedIDs
SELECT
#ID, Name, Contact, Address, City, State, Zip, Phone, Active, #G_Active
FROM #dTableGroup
and then you can have your second INSERT statement:
INSERT INTO dbo.table2(Table1ID, Type)
SELECT ID, #type FROM #InsertedIDs
See the MSDN docs on the OUTPUT clause for more details on what you can do with the OUTPUT clause - one of the most underused and most "unknown" features of SQL Server these days!
Another approach using OUTPUT clause and only one statement for inserting data in both destination tables:
--Parameters
DECLARE #TableGroup TABLE
(
Name NVARCHAR(100) NOT NULL
,Phone VARCHAR(10) NOT NULL
);
DECLARE #Type INT;
--End Of parameters
--Destination tables
DECLARE #FirstDestinationTable TABLE
(
FirstDestinationTableID INT IDENTITY(1,1) PRIMARY KEY
,Name NVARCHAR(100) NOT NULL
,Phone VARCHAR(10) NOT NULL
);
DECLARE #SecondDestinationTable TABLE
(
SecondDestinationTable INT IDENTITY(2,2) PRIMARY KEY
,FirstDestinationTableID INT NOT NULL
,[Type] INT NOT NULL
,CHECK([Type] > 0)
);
--End of destination tables
--Test1
--initialization
INSERT #TableGroup
VALUES ('Bogdan SAHLEAN', '0721200300')
,('Ion Ionescu', '0211002003')
,('Vasile Vasilescu', '0745600800');
SET #Type = 9;
--execution
INSERT #SecondDestinationTable (FirstDestinationTableID, [Type])
SELECT FirstINS.FirstDestinationTableID, #Type
FROM
(
INSERT #FirstDestinationTable (Name, Phone)
OUTPUT inserted.FirstDestinationTableID
SELECT tg.Name, tg.Phone
FROM #TableGroup tg
) FirstINS
--check records
SELECT *
FROM #FirstDestinationTable;
SELECT *
FROM #SecondDestinationTable;
--End of test1
--Test2
--initialization
DELETE #TableGroup;
DELETE #FirstDestinationTable;
DELETE #SecondDestinationTable;
INSERT #TableGroup
VALUES ('Ion Ionescu', '0210000000')
,('Vasile Vasilescu', '0745000000');
SET #Type = 0; --Wrong value
--execution
INSERT #SecondDestinationTable (FirstDestinationTableID, [Type])
SELECT FirstINS.FirstDestinationTableID, #Type
FROM
(
INSERT #FirstDestinationTable (Name, Phone)
OUTPUT inserted.FirstDestinationTableID
SELECT tg.Name, tg.Phone
FROM #TableGroup tg
) FirstINS
--check records
DECLARE #rc1 INT, #rc2 INT;
SELECT *
FROM #FirstDestinationTable;
SET #rc1 = ##ROWCOUNT;
SELECT *
FROM #SecondDestinationTable;
SET #rc2 = ##ROWCOUNT;
RAISERROR('[Test2 results] #FirstDestinationTable: %d rows; ##SecondDestinationTable: %d rows;',1,1,#rc1,#rc2);
--End of test1
Since you need all inserted identity values, look at the output clause of the insert statement: http://msdn.microsoft.com/en-us/library/ms177564.aspx

How to merge XML in T-SQL?

It doesn't seem that any amount of reading the docs will help me. Consider the simplified example:
declare #table1 table ( id int, parent xml )
insert #table1 values( 1, '<Root></Root>' )
declare #table2 table ( id int, guts xml )
insert #table2 values( 1, '<Guts>hi mom!</Guts>' )
select t1.parent.query('')
from #table1 t1 inner join #table2 t2 on t1.id = t2.id
What would be passed to the query function to generate this result?
<Root><Guts>hi mom!</Guts></Root>
The following is not set based, but maybe it will help (SQL2008 only)
declare #table1 table ( id int, parent xml )
insert #table1 values( 1, '<Root></Root>' )
declare #table2 table ( id int, guts xml )
insert #table2 values( 1, '<Guts>hi mom!</Guts>' )
DECLARE #id int;
DECLARE #results table (id int, results xml);
DECLARE idCursor CURSOR FOR
select id from #table1
OPEN idCursor
FETCH NEXT FROM idCursor INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #parent xml, #guts xml
SELECT #parent = parent FROM #table1 where id = 1;
SELECT #guts = guts FROM #table2 where id = 1;
SET #parent.modify('insert sql:variable("#guts") into (/Root[1])');
INSERT #results (id, results) values (#id, #parent);
FETCH NEXT FROM idCursor INTO #id
END
CLOSE idCursor
DEALLOCATE idCursor
select * from #results;
You are asking for an XML operation, not for a relational operation. What you want is to produce a new XML by inserting a fragment of XML into it, which means you have to use the xml.modify() method. Technically this is possible, but the modify() must be called within an update context, so it won't work in a SELECT. It can work in a SET or in an UPDATE:
UPDATE t1
SET parent.modify(N'insert sql:column("t2.guts") into (/Root)[1]')
FROM #table1 t1
JOIN #table2 t2 on t1.id = t2.id;
SELECT * from #table1;
If you must have the result in a SELECT then you'll have to shred the XML into relational table, join that and reconstruct the XML back using FOR XML.

How can I transform a table with 1 row with varchar columns into a column?

In SQL server How can I transform 1 row with varchar columns into a column?
I think I need to use Pivot but I can't find an example without agregators
this is the situation I have:
create table #tmp ( ac varchar(100), bc varchar(100), cc varchar(100))
insert into #tmp (ac,bc,cc)
Values ('test1','test2','test3')
insert into #tmp
Values ('test4','test5','test6')
SELECT *
FROM #tmp
WHERE ac='test1'
drop table #tmp
I need to transform the result of the select in a column, I will use the result column in another nested query.
Thank you!
You need to use UNPIVOT, not PIVOT
create table #tmp ( ac varchar(100), bc varchar(100), cc varchar(100))
insert into #tmp (ac,bc,cc)
Values ('test1','test2','test3')
SELECT
*
FROM
#tmp
SELECT
*
FROM
#tmp
UNPIVOT
(
[Column] FOR Data IN (ac, bc, cc)
) uPIVOT
drop table #tmp

Resources