I have a stored procedure that takes data from a table and creates a record in another table in the following structure:
TableA = Source Data
TableB = Destination 1
First, we query all the data we need from the source table and insert it into TableB. This table has an identity called recordID.
This is done through an INSERT from a Select statement which could contain a variable amount of records.
When this is complete, I need to update a column in TableA called TableBRef with the recordID that was created from the insert in TableB.
I tried using Scope_Identity() but because its inserting multiple records, it only gets the ID of the last record.
I also tried to create a SQLFiddle but it appears the site is having issues as I am getting the error Unknown Error Occurred: XML document structures must start and end within the same entity.: on even the sample fiddle.
Any recommendations to be able to accomplish what I am needing?
Update:
Here is some example code since SQLFiddle is down:
-- This is our source data
DECLARE #source TABLE (recordID INT IDENTITY (1,1), name VARCHAR(100), phone VARCHAR(20));
INSERT INTO #source(name , phone)
VALUES (
'Bob Desk', '123-456-7899',
'Don Mouse', '123-456-5555',
'Mike Keyboard', '123-456-7899',
'Billy Power', '122-222-1134'
)
-- This is the first step in the process - Inserting the records into our table
DECLARE #data1 TABLE (recordID INT IDENTITY (1,1), name VARCHAR(100) NOT NULL, phone VARCHAR(20) NOT NULL, sourceID INT NULL)
SELECT name, phone
FROM #source;
-- Based on some condition, we take records from #data1 and insert them into #data2
DECLARE #data2 TABLE (recordID INT IDENTITY (1,1), name VARCHAR(100) NOT NULL, phone VARCHAR(20) NOT null)
INSERT INTO #data2( name, phone)
SELECT name, phone
FROM #data1
WHERE phone <> '123-456-5555'
-- I now need to update #data1 with the recordID that was created from inserting the data into #data2
UPDATE #data1 SET SOURCEID = 'blah'
Lets setup some tables:
DECLARE #source TABLE
(
recordID INT IDENTITY (1,1),
name VARCHAR(100),
phone VARCHAR(20),
sourceID INT
);
This will store all of the updates coming out of the insert statement:
DECLARE #NewRecord TABLE
(
recordID INT,
name VARCHAR(100),
phone VARCHAR(20)
);
Now we insert the records, and we output the updated record into a table variable to get the new id's:
INSERT INTO #source (name, phone)
OUTPUT inserted.recordid, inserted.name, inserted.phone INTO #NewRecord
VALUES
('Bob Desk', '123-456-7899'),
('Don Mouse', '123-456-5555'),
('Mike Keyboard', '123-456-7899'),
('Billy Power', '122-222-1134')
Here is the output:
SELECT * FROM #NewRecord
recordID name phone
1 Bob Desk 123-456-7899
2 Don Mouse 123-456-5555
3 Mike Keyboard 123-456-7899
4 Billy Power 122-222-1134
Becuase when we are inserting data into data2 with new identity column so we lost source record id so I used name and phone to find what I had inserted --this might not work properly if there are duplicate name and phone seems like there is some design flaw.
but here is some thing you can try this below
-- This is our source data
DECLARE #source TABLE (recordID INT IDENTITY (1,1), name VARCHAR(100), phone VARCHAR(20));
INSERT INTO #source(name , phone)
VALUES (
'Bob Desk', '123-456-7899'),
('Don Mouse', '123-456-5555'),
('Mike Keyboard', '123-456-7899'),
('Billy Power', '122-222-1134')
-- This is the first step in the process - Inserting the records into our table
DECLARE #data1 TABLE (recordID INT IDENTITY (1,1), name VARCHAR(100) NOT NULL, phone VARCHAR(20) NOT NULL, sourceID INT NULL)
--Capture inserts using another table
DECLARE #cdcapture1 TABLE (recordID INT , name VARCHAR(100) NOT NULL, phone VARCHAR(20) NOT null)
INSERT INTO #data1(name,phone)
OUTPUT Inserted.recordID,Inserted.name,Inserted.phone INTO #cdcapture1
SELECT name, phone
FROM #source;
--Capture inserts using another table
DECLARE #cdcapture2 TABLE (recordID INT , name VARCHAR(100) NOT NULL, phone VARCHAR(20) NOT null)
-- Based on some condition, we take records from #data1 and insert them into #data2
DECLARE #data2 TABLE (recordID INT IDENTITY(1,1), name VARCHAR(100) NOT NULL, phone VARCHAR(20) NOT null)
INSERT INTO #data2( name, phone)
OUTPUT Inserted.* INTO #cdcapture2
SELECT name, phone
FROM #cdcapture1 c1
--WHERE phone <> '123-456-5555'
-- Becuase when we are inserting data into data2 with new identity column so we lost source record id
UPDATE d1
SET d1.sourceid = c2.recordid
FROM #data1 d1 INNER JOIN #cdcapture1 c1 ON d1.recordID = c1.recordID
INNER JOIN #cdcapture2 c2 ON c2.NAME = c1.NAME AND c2.phone = c1.phone
SELECT * FROM #data1
-- This is our source data
DECLARE #source TABLE
(
recordID INT IDENTITY (1,1),
name VARCHAR(100),
phone VARCHAR(20)
);
INSERT INTO #source(name , phone)
VALUES
('Bob Desk', '123-456-7899'),
('Don Mouse', '123-456-5555'),
('Mike Keyboard', '123-456-7899'),
('Billy Power', '122-222-1134');
-- This is the first step in the process - Inserting the records into our table
DECLARE #data1 TABLE
(
recordID INT IDENTITY (1,1),
name VARCHAR(100) NOT NULL,
phone VARCHAR(20) NOT NULL,
sourceID INT NULL
)
INSERT INTO #data1 (name, phone )
SELECT name, phone
FROM #source;
-- Based on some condition, we take records from #data1 and insert them into #data2
DECLARE #data2 TABLE
(
recordID INT IDENTITY (1,1),
name VARCHAR(100) NOT NULL,
phone VARCHAR(20) NOT null,
data1recid INT
)
INSERT INTO #data2( name, phone, data1recid)
SELECT name, phone, recordID
FROM #data1
WHERE phone <> '123-456-5555'
-- I now need to update #data1 with the recordID that was created from inserting the data into #data2
UPDATE m
set m.sourceID = d.recordID
FROM #data1 m
INNER JOIN #data2 d
ON m.recordID = d.data1recid
Here is the output:
SELECT * FROM #data1
recordID name phone sourceID
1 Bob Desk 123-456-7899 1
2 Don Mouse 123-456-5555 NULL
3 Mike Keyboard 123-456-7899 2
4 Billy Power 122-222-1134 3
Is this more along the lines of what you needed?
Related
CREATE TABLE projeto.Person (
Person_Code INT IDENTITY(1,1) NOT NULL,
birthDate DATE NOT NULL,
Name VARCHAR(50) NOT NULL,
PRIMARY KEY (Person_Code)
)
CREATE TABLE projeto.Student (
Student_Code INT REFERENCES projeto.Person (Person_Code),
payment INT NOT NULL,
PRIMARY KEY (Student_Code),
)
CREATE TABLE projeto.teacher (
payment INT NOT NULL,
teacher_Code INT,
PRIMARY KEY (teacher_Code),
CHECK (payment > 350)
)
How do I insert values in student, paying attention that a student has all person attributes? e.g. student has name, birth_date etc.
I tried this:
INSERT INTO projeto.Person VALUES
('1961-03-26', John Adam')
but this only adds in a person, and I can't tell if its a student or not.
I guess its how to get the recently inserted Person_Code that you are asking? In which case use scope_identity().
declare #BirthDate date, #Name varchar(50), #Payment int, #IsStudent bit, #IsTeacher bit, #NewPersonCode int;
-- SET THE RELEVANT VARIABLE VALUES
-- Insert person
insert into projeto.Person (BirthDate, [Name])
select #BirthDate, #Name;
-- Get the Person_Code of the new person record
set #NewPersonCode = scope_identity();
-- Insert Student record if a student
insert into projeto.Student (Student_Code, Payment)
select #NewPersonCode, #Payment
where #IsStudent = 1;
-- Insert Teacher record if a teacher
insert into projeto.Teacher (Teacher_Code, Payment)
select #NewPersonCode, #Payment
where #IsTeacher = 1;
I have a temp table that looks like this:
FirstName
LastName
DOB
Sex
Age
ExternalID
In my stored procedure I'm inserting these values into a regular table that has the following structure:
ID identity(1,1)
FirstName
LastName
So, I do this:
Insert into myTable
select FirstName, LastName from TempTable
During the insert I need to insert primary key from main table back into temp table "ExternalID" column. How can this be achieved?
I tried using OUTPUT statement but it only allows to insert to a separate table and then I have no way to map back to temp table
I need to insert generated IDs to column ExternalID in temp table right after the insert. FirstName and LastName are not unique.
One possible solution would be to use loop and insert one row at a time. This way, I can update temp table row with scope_identity(). But I want to avoid using loops.
Try using MERGE instead of INSERT.
MERGE allows you to output a column you didn't insert, such as an identifier on your temp table. Using this method, you can build another temporary table that maps your temp table to the inserted rows (named #TempIdTable in the sample below).
First, give #TempTable its own primary key. I'll call it TempId. I'll also assume you have a column on #TempTable to store the returned primary key from MyTable, ID.
--Make a place to store the associated ID's
DECLARE #TempIdTable TABLE
([TempId] INT NOT NULL
,[ID] INT NOT NULL)
--Will only insert, as 1 never equals 0.
MERGE INTO myTable
USING #TempTable AS tt
ON 1 = 0
WHEN NOT MATCHED
THEN
INSERT ([FirstName]
,[LastName])
VALUE (t.[FirstName]
,t.[LastName])
OUTPUT tt.[TempId], inserted.[ID] --Here's the magic
INTO #TempIdTable
--Associate the new primary keys with the temp table
UPDATE #TempTable
SET [ID] = t.[ID]
FROM #TempIdTable t
WHERE #TempTable.[TempId] = t.[TempId]
I was working on a similar issue and found this trick over here: Is it possible to for SQL Output clause to return a column not being inserted?
Here's the full code I used in my own testing.
CREATE TABLE [MQ]
([MESSAGEID] INT IDENTITY PRIMARY KEY
,[SUBJECT] NVARCHAR(255) NULL);
CREATE TABLE [MR]
([MESSAGESEQUENCE] INT IDENTITY PRIMARY KEY
,[TO] NVARCHAR(255) NOT NULL
,[CC] NVARCHAR(255) NOT NULL
,[BCC] NVARCHAR(255) NOT NULL);
CREATE TABLE #Messages (
[subject] nvarchar(255) NOT NULL
,[to] nvarchar(255) NOT NULL
,[cc] nvarchar(255) NULL
,[bcc] nvarchar(255) NULL
,[MESSAGEID] INT NULL
,[sortKey] INT IDENTITY PRIMARY KEY
);
INSERT INTO #Messages
VALUES ('Subject1','to1','cc1','bcc1', NULL)
,('Subject2','to2', NULL, NULL, NULL);
SELECT * FROM #Messages;
DECLARE #outputSort TABLE (
[sortKey] INT NOT NULL
,[MESSAGEID] INT NOT NULL
,[subject] NVARCHAR(255)
);
MERGE INTO [MQ]
USING #Messages M
ON 1 = 0
WHEN NOT MATCHED
THEN
INSERT ([SUBJECT])
VALUES (M.[subject])
OUTPUT M.[SORTKEY]
,inserted.[MESSAGEID]
,inserted.[SUBJECT]
INTO #outputSort;
SELECT * FROM #outputSort;
SELECT * FROM [MQ];
UPDATE #Messages
SET MESSAGEID = O.[MESSAGEID]
FROM #outputSort O
WHERE #Messages.[sortKey] = O.[sortKey];
SELECT * FROM #Messages;
DROP TABLE #Messages;
As you said, FirstName and LastName are not unique. This means you cannot use a trigger because there can be the same FirstName + LastName so you cannot join on them.
But you can do the inverse thing: first update your temp table ExternalID (I suggest you to use sequence object and just do update #t set ExternalID = next value for dbo.seq1;) and then just insert your rows including ExternalID into myTable. To be able to insert into identity field you can use set identity_insert myTable on or you can re-design your destination table to contain no identity at all as now you use sequence for the same purpose.
We need a unique column for able to make the comparison at the update operation after the insert. That's why we are using ExternalID column temporarily. ExternalID updated by row_nubmber.
;WITH CTE AS
(
SELECT *, RN = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM #TempTable
)
UPDATE CTE SET ExternalID = RN
We are keeping the output of the insert operation in a temp table. The trick is order by with ExternalID, it will help us for making the unique row number for same first and last name
DECLARE #output TABLE (
ID INT,
FirstName VARCHAR(10),
LastName VARCHAR(10))
Insert into #myTable
OUTPUT inserted.ID, inserted.FirstName, inserted.LastName INTO #output(ID, FirstName, LastName)
select FirstName, LastName from #TempTable T
order by ExternalID
For replacing the ExternalID column with inserted id value, we are making comparing with first name, last name and row number.
;WITH TMP_T AS(
SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY FirstName, LastName ORDER BY ExternalID) FROM #TempTable )
,OUT_T AS(
SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY FirstName, LastName ORDER BY ID) FROM #output )
UPDATE TMP_T SET ExternalID = OUT_T.ID
FROM
TMP_T INNER JOIN OUT_T ON
TMP_T.FirstName = OUT_T.FirstName
AND TMP_T.LastName = OUT_T.LastName
AND TMP_T.RN = OUT_T.RN
Sample Data:
DECLARE #TempTable TABLE (
FirstName VARCHAR(10),
LastName VARCHAR(10),
DOB VARCHAR(10),
Sex VARCHAR (10),
Age VARCHAR(10),
ExternalID INT)
INSERT INTO #TempTable VALUES
('Serkan1', 'Arslan1', 'A','M','1',NULL),
('Serkan2', 'Arslan2', 'B','M','1',NULL),
('Serkan3', 'Arslan', 'C','M','1',NULL),
('Serkan3', 'Arslan', 'D','M','1',NULL)
DECLARE #myTable TABLE (
ID INT identity(100,1), -- started from 100 for see the difference
FirstName VARCHAR(10),
LastName VARCHAR(10))
Result:
MyTable
ID FirstName LastName
----------- ---------- ----------
100 Serkan1 Arslan1
101 Serkan2 Arslan2
102 Serkan3 Arslan
103 Serkan3 Arslan
TempTable
FirstName LastName DOB Sex Age ExternalID
---------- ---------- ---------- ---------- ---------- -----------
Serkan1 Arslan1 A M 1 100
Serkan2 Arslan2 B M 1 101
Serkan3 Arslan C M 1 102
Serkan3 Arslan D M 1 103
One way to do this is by duplicating the data into a second temp table like so:
SELECT *
INTO #TEMPTABLE
FROM (VALUES (1, 'Adam'), (2, 'Kate'), (3, 'Jess')) AS X (Id, Name)
SELECT TOP 0 CAST(NULL AS INT) AS IdentityValue, *
INTO #NEWTEMPTABLE
FROM #TEMPTABLE
CREATE TABLE #TABLEFORINSERT (
IdentityColumn INT IDENTITY(1,1),
Id INT,
Name VARCHAR(255)
)
INSERT INTO #TABLEFORINSERT (Id, Name)
OUTPUT INSERTED.IdentityColumn, INSERTED.Id, Inserted.Name INTO #NEWTEMPTABLE
SELECT Id, Name FROM #TEMPTABLE
--New temp table with identity values
SELECT * FROM #NEWTEMPTABLE
create table sample(id int primary key,name varchar(100))
insert into sample values(1,'a')
,(2,'a')
,(3,'d')
,(4,'b')
,(5,'b')
--insert into sample values(6,'a'),(7,'b')
this record is not allow to insert the table.it disply error
Easiest solution is to check the table if the value being inserted is already present in the table twice before the insert statement.
--Preparation
DECLARE #sample TABLE
(
id INT IDENTITY PRIMARY KEY --USE IDENTITY to auto increment your primary key
,name VARCHAR(100)
)
--Initial Values
INSERT INTO #sample
VALUES
('a')
,('a')
,('d')
,('b')
,('b')
DECLARE #name VARCHAR(100)
SET #name = 'a'
IF(SELECT COUNT(id) FROM #sample WHERE name = #name) <= 1
BEGIN
INSERT INTO #sample
VALUES (#name)
END
ELSE
BEGIN
SELECT 'Error: Name ''' + #name + ''' already exists twice. Only two same values are allowed in name field!'
END
database diagram
For sql-server
How can I insert a SKU row into table SKU_DATA, then insert the SKU into INVENTORY table with all branches and Quantity on hand=2 and Quantityonhand=0.
I need to insert a SKU item into SKUDATA and correspond row in inventory table for all existing branches Quantityonhand=2 and Quantityonhand = 0.
Please help Thanks
BRANCH
name varchar (30) not NULL,
managerNum INT NOT NULL,
SKU_DATA
SKU Int NOT NULL IDENTITY (1000,1),
description varchar (40) NOT NULL UNIQUE,
department varchar(30) NOT NULL default 'Home Entertainment',
sellingPrice numeric (7,2) NOT NULL ,
INVENTORY
SKU Int NOT NULL,
branch varchar (30) NOT NULL ,
quantityOnHand Int NOT NUll ,
quantityOnOrder Int NOT NUll ,
Procedure:
Create procedure InsertNewSkuWithInventory
#description varchar (40),
#department varchar(30),
#sellingPrice numeric (7,2),
AS
Declare #rowcount as int
Declare #SKU as int
Declare #branch as varchar(30)
Select #rowcount = COUNT(*)
from dbo.SKU_DATA
Where description = #description
And department = #department
And sellingPrice = #sellingPrice;
BEGIN
INSERT INTO dbo.SKU_DATA (description, department, sellingPrice)
VALUES (#description, #department, #sellingPrice);
Select #SKU =SKU
From dbo.SKU_DATA
Where description = #description
And department = #department
And sellingPrice = #sellingPrice;
DECLARE SKUCursor CURSOR FOR
SET #branch = ##IDENTITY
Select SKU
From dbo.inventory
Where
CLOSE SKUCursor
DEALLOCATE SKUCursor
END
There are two problems here: first, how to get the generated IDENTITY value for the newly inserted row into SKU_DATA table, and second, how to one row into INVENTORY table for each branch.
If you're inserting only a single row into SKU_DATA, then use the scope_identity() function (never use the ##IDENTITY!). Like this:
INSERT INTO dbo.SKU_DATA (description, department, sellingPrice)
VALUES (#description, #department, #sellingPrice);
set #SKU = scope_identity()
Then you do insert into INVENTORY by selecting from BRANCH table:
insert dbo.INVENTORY (SKU, branch, quantityOnHand, quantityOnOrder)
select #SKU, b.name, 2, 0
from dbo.Branch b
Hope you can work the complete solution out from this.
I am trying to insert data into many tables in one SQL Server stored procedure. I am also using the identities from the tables that I have inserted data into to then resolve the many to many relationship by writing those identities to another table.
In theory the logic seems to be there for the stored procedure, but on execution only the first insert statement has been executed. Please could anyone assist with this.
The stored procedure is as follows:
Create Procedure [dbo].[InsertAllCustomerDetails]
(
--#CustomerID Bigint output,
#Firstname varchar(100),
#LastName varchar(100),
#Initials varchar(10),
#Title varchar(20),
#DateCreated datetime,
#isDeleted Bit,
--#ContactNumberID BIGINT Output,
#ContactNumber Varchar(100),
#ContactTypeID bigint,
#Street Varchar(550),
#AreaID BIGINT,
#isPreferred Bit
--#AddressID Bigint OutPut
)
AS
Insert Into Customer
(
FisrtName,
LastName,
Initials,
[Title],
DateCreated,
isDeleted
)
Values
(
#Firstname,
#LastName,
#Initials,
#Title,
#DateCreated,
#isDeleted
)
Declare #CustomerID BIGINT
SELECT #CustomerID = ##IDENTITY
RETURN #CustomerID
--This will now insert the contact details for the customer
Insert Into ContactNumber
(
ContactNumber,
ContactTypeID
)
Values
(
#ContactNumber,
#ContactTypeID
)
Declare #ContactNumberID BIGINT
SELECT #ContactNumberID = ##IDENTITY
--This will insert into the CustomerContactNumber
Insert Into CustomerContactNumber
(
ContactNumberID,
CustomerID
)
Values
(
#ContactNumberID,
#CustomerID
)
--This will insert the address
Insert Into [Address]
(
Street,
AreaID,
isPreferred
)
Values
(
#Street,
#AreaID,
#isPreferred
)
Declare #AddressID BIGINT
SELECT #AddressID = ##IDENTITY
--This will insert the relationship for the customer Address table
Insert into CustomerAddress
(
CustomerID,
AddressID
)
Values
(
#CustomerID,
#AddressID
)
I see two things:
You seem to have a typo in the Customer insert:
Insert Into Customer
(
FisrtName, <-- should be FirstName?
LastName,
You are RETURNing after the Customer insert - that's why only the first one runs
Declare #CustomerID BIGINT
SELECT #CustomerID = ##IDENTITY
RETURN #CustomerID <---- This exits the sproc
--This will now insert the contact details for the customer
Insert Into ContactNumber
I'm guessing the RETURN was there for debugging and not removed since it's obscured by the indentation.