How to group several INSERTs? - sql

I'm inserting data from one table into several others. The first insert will create a new userid. This new userid will be used in succeeding inserts. I will also continue inserting username from the source table into other tables. The chain of inserts below are for one user. There will be probably 2000 users involved.
I'm familiar with how this can be done using a cursor. Is there some other way to do this chain of inserts without a cursor?
insert into table 1 using #username and #firstname from source table
insert into table 2 using userid generated from table 1 (userid1)
insert into table 3 using #username and userid1
insert into table 4 using userid1

You can use the Output Clause of an Insert statement to capture generated Ids in bulk.
For example:
Create Table dbo.Source (
FirstName nvarchar(100),
LastName nvarchar(100)
);
Create Table dbo.Attrs (
Id int Identity Not Null Primary Key,
Name nvarchar(100) Not Null,
DefaultVal nvarchar(100)
);
Create Table dbo.Table1 (
Id Int Identity Not Null Primary Key,
FirstName nvarchar(100),
LastName nvarchar(100)
);
Create Table dbo.Table2 (
Id int Identity Not Null Primary Key,
Table1ID int Not Null Foreign Key References dbo.Table1 (Id),
AttrId int Not Null Foreign Key References dbo.Attrs (Id)
);
Insert Into dbo.Source Values
(N'Mickey', N'Mouse'),
(N'Donald', N'Duck'),
(N'Goofy', Null);
Insert Into dbo.Attrs Values
('Size', 'Small'),
('Wings', 'No');
Declare #Temp1 Table (Id Int, FirstName nvarchar(100), LastName nvarchar(100))
Declare #Temp2 Table (Id int, Table1ID int, AttrId int)
Insert Into dbo.Table1
(FirstName, LastName)
Output
inserted.Id, inserted.FirstName, inserted.LastName
Into
#Temp1
Select
FirstName, LastName
From
dbo.Source
Insert Into dbo.Table2
(Table1ID, AttrId)
Output
inserted.Id, Inserted.Table1ID, Inserted.AttrID
Into
#Temp2
Select
t.Id,
a.Id
From
#Temp1 t
Cross Join
dbo.Attrs a
Select * From #Temp2
http://sqlfiddle.com/#!3/31110/3

Related

How to insert data into another table when the condition is met with trigger

I have these 4 tables:
CREATE TABLE dbo.person
(
personId INT IDENTITY(1,1) NOT NULL,
firstName NVARCHAR(30) NOT NULL,
lastName NVARCHAR(30) NOT NULL,
CONSTRAINT pkPerson PRIMARY KEY (personId),
);
CREATE TABLE dbo.personRegistration
(
person_registrationId INT IDENTITY(1,1) NOT NULL,
personId INT,
firstName NVARCHAR(30) NOT NULL,
lastName NVARCHAR(30) NOT NULL,
confirmed NCHAR(1) DEFAULT 'N' NOT NULL,
CONSTRAINT pkpersonRegistration PRIMARY KEY (person_registrationId),
CONSTRAINT fkpersonRegistration FOREIGN KEY (personId) REFERENCES dbo.person (personId)
CONSTRAINT personConfirmed CHECK (confirmed IN ('Y', 'N'))
);
CREATE TABLE dbo.person_organizationalUnit
(
personId INT NOT NULL,
organizationalUnitId INT NOT NULL,
CONSTRAINT pkorganizationalUnit PRIMARY KEY (personId, organizationalUnitId),
CONSTRAINT fkperson FOREIGN KEY (personId) REFERENCES dbo.person (personId),
CONSTRAINT fkorganizationalUnit FOREIGN KEY (organizationalUnitId) REFERENCES dbo.organizatinalUnit(unicOrgUnitId),
);
CREATE TABLE dbo.organizatinalUnit
(
organizationalUnitId INT IDENTITY(1,1) NOT NULL,
organizationalUnitName NVARCHAR(130) NOT NULL,
CONSTRAINT pkorganizationalUnit PRIMARY KEY (organizationalUnitId)
);
I need to create a trigger which will do that when I add new person in table personRegistration (his personId is set to NULL, and initial value for confirmed is 'N') and when I update personRegistration and set confirmed to 'Y', that person is going to be inserted into table person (value for personId is generated because the personId is an identity column) and the confirmed is going to change it's value to 'Y' and is going to be inserted in table person_organizationalUnit. I have written the trigger but the problem is when I update the personRegistration for more than one person my data double with each update.
CREATE TRIGGER personConfirmed
ON dbo.personRegistration
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON
INSERT INTO dbo.person (firstName, lastName)
SELECT
firstName, lastName
FROM
dbo.personRegistration
SET NOCOUNT ON
DECLARE #idPerson int
SELECT #idPerson = personId
FROM dbo.person
INSERT INTO dbo.person_organizationalUnit (personId, organizationalUnitId)
SELECT #idPerson, I.organizationalUnitId
FROM Inserted AS I
JOIN dbo.person p ON p.personId = #idPerson
WHERE confirmed = 'Y';
END
Data for insert:
INSERT INTO dbo.personRegistration (personId, firstName, lastName, confirmed)
VALUES (NULL, 'John', 'Smith', 'N');
Data for update:
UPDATE dbo.personRegistration
SET confirmed = 'Y'
WHERE personRegistrationId = 1;
SQL Server triggers works with sets not single register i did some small changes in your trigger
create TRIGGER dbo.usp_PersonConfirmed ON dbo.personRegistration
AFTER UPDATE
AS
BEGIN
-- create person if not exists
INSERT INTO dbo.person (firstName, lastName)
SELECT firstName, lastName
FROM dbo.personRegistration p
where not exists(select * from dbo.Person where firstName = p.firstName
and lastName = p.lastName)
-- create orgonization unit if person dont exist and confirmed is Y
INSERT INTO dbo.person_organizationalUnit (personId, organizationalUnitId)
SELECT i.personId, I.organizationalUnitId
FROM Inserted AS I
where not exists(select * from dbo.person_organizationalUnit where
personId = i.personId)
and confirmed = 'Y';
-- update orgonization unit if person exist and confirmed is Y
update pou set organizationalUnitId = I.organizationalUnitId
from dbo.person_organizationalUnit pou
inner join Inserted AS I on i.personID = pou.personId
where i.confirmed = 'Y';
END
Your trigger has a fatal flaw: it does not deeal properly with multiple rows. It is also not using the inserted table in the first INSERT, and instead selecting from the whole original table.
So you need to OUTPUT the identity column from the first insert in order to use it in the second.
Because you don't have the identity column yet, you need to join onto firstName and lastName, which I need not say isn't a very good primary key
CREATE OR ALTER TRIGGER personConfirmed
ON dbo.personRegistration
AFTER UPDATE
AS
SET NOCOUNT ON
IF NOT UPDATE(confirmed) OR NOT EXISTS (SELECT 1 FROM inserted)
RETURN; --early bailout
DECLARE #ids TABLE (personId int PRIMARY KEY, firstName nvarchar(100), lastName nvarchar(100));
INSERT INTO dbo.person (firstName, lastName)
OUTPUT inserted.personId, inserted.firstName, inserted.lastName
SELECT
i.firstName,
i.lastName
FROM
inserted i
WHERE i.confirmed = 'Y';
INSERT INTO dbo.person_organizationalUnit (personId, organizationalUnitId)
SELECT ids.personId, i.organizationalUnitId
FROM inserted AS i
JOIN #ids ids ON i.firstName = ids.firstName AND i.lastName = ids.lastName;
Ideally, you have some kind of unique primary key on personRegistration. then your trigger would look like this:
CREATE OR ALTER TRIGGER personConfirmed
ON dbo.personRegistration
AFTER UPDATE
AS
SET NOCOUNT ON
IF NOT UPDATE(confirmed) OR NOT EXISTS (SELECT 1 FROM inserted)
RETURN; --early bailout
DECLARE #ids TABLE (personId int PRIMARY KEY, registrationId int);
MERGE dbo.person p
USING (
SELECT *
FROM inserted i
WHERE i.confirmed = 'Y'
) i
ON 1 = 0 -- never match
WHEN NOT MATCHED THEN
INSERT (firstName, lastName)
VALUES (i.firstName, i.lastName)
OUTPUT inserted.personId, i.organizationalUnitId
INTO #ids (personId, organizationalUnitId)
;
INSERT INTO dbo.person_organizationalUnit (personId, organizationalUnitId)
SELECT ids.personId, i.organizationalUnitId
FROM #ids ids;
We need that funny MERGE because we want to OUTPUT columns that we are not inserting. You can only do this using MERGE.

Insert into multiple(7) tables with no duplicates

Trying to create a query/SP that will take data from one table and insert it into multiple tables. I have one main table that everything is put into at the beginning like a temp table.
Temp table
CREATE TABLE Employee
(
userID INT IDENTITY(1,1) NOT NULL,
userName VARCHAR(50) NULL,
FirstName VARCHAR(50) NULL,
LastName VARCHAR(50) NUll,
UserPassWd VARCHAR(50) NULL,
EmailId VARCHAR(100) NULL
CONSTRAINT PK_Employee PRIMARY KEY (userID)
)
Than when employee is verified it will be split up into multiple tables that only need a field or two from the temp table as needed. The UserEmail table I have listed below is one of the tables. I'm trying to get it to work for one table right now and then I'm guessing i will just copy the insert part and change the table name and attributes to the new tables
Here is what i have so far.
DECLARE #EMAIL VARCHAR(100)
DECLARE #USERID INT
SELECT #USERID = userID
,#EMAIL = EmailId
FROM Employee
WHERE userID = 1004
INSERT INTO UserEmail
(
EmailAddress
,EmailTypeID
,ExternalUserID
,Active
,CreatedByID
,CreatedDate
,UpdatedByID
,UpdatedDate
)
SELECT #EMAIL -- Email Address
,1 -- Email Type
,1 -- ExternalUserID
,1 -- Active
,1 -- CreatedByID
,CURRENT_TIMESTAMP -- CreatedDate
,1
,CURRENT_TIMESTAMP -- UpdatedDate
FROM Employee X
WHERE 1=1
AND X.userID = '####'-- INSERT USERID HERE for testing
This will insert the record into the UserEmail table but will create duplicate users, which i cant have so I tried adding this but it doesn't do what I want it to do.
WHERE 1=1
AND NOT EXISTS(
SELECT userID
FROM Employee
WHERE userID = 1004
)
Any guidance or help would be much appreciated. Thank You!
If you only like NOT to insert to UserEmail if user already exists just extend
INSERT INTO UserEmail ....
SELECT ....
FROM ....
WHERE ..
AND NOT EXISTS (select 1 from UserEmail where EmailAddress = X.emailAddress)
Otherwise review MERGE syntax (https://learn.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql)

If exists update the XML column or insert into new row in reference table

If exists data into parent table then update the child table on XML column based on the reference id, other wise insert into row.
CREATE TABLE Person
(
PersonId INT CONSTRAINT PK_Person_PersonId PRIMARY KEY,
Name VARCHAR(50),
SubMittedDate DATETIME,
SubmittedBy INT,
RejectedDate DATETIME,
RejectedBy INT
)
INSERT INTO Person VALUES(1, 'Sai', GETDATE(),1,null,null)
CREATE TABLE PersonXML
(
PersonXMLId INT IDENTITY(1,1) CONSTRAINT PK_PersonXML_PersonId PRIMARY KEY,
PersonId INT CONSTRAINT FK_PersonXML_Person FOREIGN KEY REFERENCES Person(PersonId),
Name VARCHAR(50),
PersonHistory XML
)
INSERT INTO PersonXML(PersonId,Name,PersonHistory )
SELECT PersonId
,Name
,(SELECT PersonId
,Name
,SubMittedDate
,SubmittedBy
,ISNULL(RejectedDate,0) AS RejectedDate
,ISNULL(RejectedBy, 0) AS RejectedBy
FROM Person FOR XML PATH('PersonHistory') )
FROM Person
SELECT * FROM Person
SELECT * FROM PersonXML
Whenever i Update Person Table in RejectedDate, RejectedBy Column then those changes has to reflect in PersonXML table on PersonHistory column automatically could anyone please find me way to achieve it.
UPDATE Person SET RejectedDate = GETDATE(), RejectedBy= 100 WHERE PersonId = 1
DECLARE #PersonId int,#RejectedDate datetime,#RejectedBy int
SET #PersonId=1
SET #RejectedDate=Getdate()+1
SET #RejectedBy=105
CREATE TABLE Person
(
PersonId INT CONSTRAINT PK_Person_PersonId PRIMARY KEY,
Name VARCHAR(50),
SubMittedDate DATETIME,
SubmittedBy INT,
RejectedDate DATETIME,
RejectedBy INT
)
INSERT INTO Person VALUES(1, 'Sai', GETDATE(),1,null,null)
CREATE TABLE PersonXML
(
PersonXMLId INT IDENTITY(1,1) CONSTRAINT PK_PersonXML_PersonId PRIMARY KEY,
PersonId INT CONSTRAINT FK_PersonXML_Person FOREIGN KEY REFERENCES Person(PersonId),
Name VARCHAR(50),
PersonHistory XML
)
INSERT INTO PersonXML(PersonId,Name,PersonHistory )
SELECT PersonId
,Name
,(SELECT PersonId
,Name
,SubMittedDate
,SubmittedBy
,ISNULL(RejectedDate,0) AS RejectedDate
,ISNULL(RejectedBy, 0) AS RejectedBy
FROM Person FOR XML PATH('PersonHistory') )
FROM Person
IF Exists(SELECT * FROM Person WHERE PersonId=#PersonId)
BEGIN
UPDATE Person
SET
RejectedDate =#RejectedDate ,RejectedBy=#RejectedBy
WHERE PersonId=#PersonId
UPDATE PersonXML
SET PersonHistory=
(SELECT PersonId
,Name
,SubMittedDate
,SubmittedBy
,ISNULL(RejectedDate,#RejectedDate) AS RejectedDate
,ISNULL(RejectedBy,#RejectedBy) AS RejectedBy
FROM Person FOR XML PATH('PersonHistory') )WHERE PersonId=#PersonId
END
ELSE
BEGIN
INSERT INTO Person
Select #PersonId,'', GETDATE(),1,#RejectedDate,#RejectedBy
END
SELECT * FROM Person
SELECT * FROM PersonXML

Insert Data Into three joined tables using a stored procedure

I have three tables [PublicationNotice], [PublicationNoticeClient] and [PublicationNoticeInvoice].
Table1 have one-to-one relation with table2 and table3.
I am using a stored procedure to insert data into the tables. My form consists of all attributes from table1, table2 and table3.
My problem is, when I Submit data into these three tables it inserts all data accept table2_id and table3_id in table1.
HINT: I am using ASP .Net and MSSQL
My stored procedure looks like this:
CREATE procedure [dbo].[AddUpdatePublicationNotice]
#ID bigint = NULL,
#Title varchar(max)= NULL,
#NewspaperName varchar(50) =NULL,
#Cities varchar(max)= NULL,
#PublicationSize varchar(8) =NULL,
#PublicationDate date =NULL,
#PublicationType varchar(50)= NULL,
#NewspaperPageNo smallint= NULL,
#Colored bit= NULL,
#CaseNature varchar(15)= NULL,
#ImagePath varchar(max)= NULL,
#ClientId bigint =NULL,
#InvoiceId bigint= NULL,
#CreatedById bigint = NULL,
#EditedById bigint= NULL,
#EditedDate datetime =NULL,
--******************************************--
#ClientName varchar(max)= NULL,
#ClientType varchar(50)= NULL,
#ClientContactPerson varchar(max)= NULL,
#ClientAddress varchar(max)= NULL,
#ClientCity varchar(50) =NULL,
#ClientCountry varchar(50)= NULL,
--******************************************--
#InvoiceDate date= NULL,
#InvoiceTotalAmount bigint= NULL,
#InvoicePaymentRecievedDate date= NULL,
#InvoiceChequeNo bigint= NULL,
#InvoiceBankName varchar(50)= NULL
--******************************************--
AS
Insert into PublicationNotice (
[Title]
,[NewspaperName]
,[Cities]
,[PublicationSize]
,[PublicationDate]
,[PublicationType]
,[NewspaperPageNo]
,[Colored]
,[CaseNature]
,[ImagePath]
,[ClientId]
,[InvoiceId]
,CreatedById
)Values(
#Title
,#NewspaperName
,#Cities
,#PublicationSize
,#PublicationDate
,#PublicationType
,#NewspaperPageNo
,#Colored
,#CaseNature
,#ImagePath
,#ClientId
,#InvoiceId
,#CreatedById)
insert into [dbo].[PublicationNoticeClient] (
[Name]
,[Type]
,[ContactPerson]
,[Address]
,[City]
,[Country]
,[CreatedById])
Values(#ClientName
,#ClientType
,#ClientContactPerson
,#ClientAddress
,#ClientCity
,#ClientCountry
,#CreatedById)
Insert Into [dbo].[PublicationNoticeInvoice] (
[Date]
,[TotalAmount]
,[PaymentRecievedDate]
,[ChequeNo]
,[BankName]
,[CreatedById])
Values (
#InvoiceDate
,#InvoiceTotalAmount
,#InvoicePaymentRecievedDate
,#InvoiceChequeNo
,#InvoiceBankName
,#CreatedById)
GO
I know I can first insert table2 and table3 values and then select the last inserted values from table2 and table3 (that are table2_id and table3_id) and then insert them into table1
Is there any other fast way to insert data like this ???
You can use ##IDENTITY , SCOPE_IDENTITY, IDENT_CURRENT or OUTPUT methods to retrieve last ID. The Output is the only secure one.
You need to insert into a table variable first and then query it
create table table1(
id int identity(1,1),
id_table2 int,
id_table3 int);
create table table2 (
id int identity(100,1),
val varchar(20));
create table table3 (
id int identity(200,1),
val varchar(20));
declare #varTable2 table (LastID int);
declare #varTable3 table (LastID int);
insert into table2
output inserted.id into #varTable2 values ('a');
insert into table3
output inserted.id into #varTable3 values ('a');
insert into table1 (id_table2, id_table3) values
( (select LastID from #varTable2),
(select LastID from #varTable3)
);
select * from table1
--You need to declare two variables to get identity values from table 2 and table 3 As
DECLARE #table2_identity AS INT
DECLARE #table3_identity AS INT
--After insert in Table 2 set table2_identity variable as follows
SET #table2_identity = SELECT SCOPE_IDENTITY()
--After insert in Table 3 set table3_identity variable as follows
SET #table3_identity = SELECT SCOPE_IDENTITY()
--Then assign those variable values in insert query of Table 1

How to insert row in table with foreign key to itself?

I have table that has foreign key for itself. Column parentid is foreign key and it cannot be NULL.
if I doINSERT INTO mytable(name) VALUES('name'), so it says that can't insert NULL to parentid. BUT, what value I can set to it if no row was inserted yet?!
How I can write script that will add row to this table?
Thank you
Remove the NOT NULL constraint, as it is an inappropriate constraint. If you do not have a ParentId then the value is NULL and should be allowed. Creating a dummy row just to have a dummy parentid creates unnecessary dependencies.
A trick: Have a dummy row with a dummy key, say 99999. Insert with this as the FK, and then change the FK to its real value. And do it in a transaction.
Disable the FK in charge.
Then do the insert
Then do an update with the new (generated?) PK-ID into the Self-FK-Field
Then Enable the FK back.
LIke so:
ALTER TABLE [Client] NOCHECK CONSTRAINT [FK_Client_MainClient]
INSERT INTO Client VALUES ...
#ClientID = SCOPE_IDENTITY()
IF #IsMainClient=1
BEGIN
UPDATE [Client]
SET MainClientID = #ClientID
WHERE ClientID = #ClientID
END
ALTER TABLE [Relatie] WITH CHECK CHECK CONSTRAINT [FK_Relatie_Relatie]
How to make a dummy row with both id and parentid equal to -1:
CREATE TABLE mytable(
id int NOT NULL IDENTITY,
parentid int NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (parentid) REFERENCES mytable(id)
) ;
SET IDENTITY_INSERT mytable ON ; <-- this allows you to insert the
INSERT INTO mytable(id, parentid) <-- auto incremented identity field
VALUES (-1, -1);
SET IDENTITY_INSERT mytable OFF ;
SELECT * FROM mytable ;
| id | parentid |
| -1 | -1 |
If you have many data from other tables that you want to transfer into this table, you can set the IDENTITY_INSERT variable to ON, insert the data and then set it to OFF again.
But as others said, it might be better to just remove the NOT NULL constraint from the parentid field.
You can alter the column to allow null then set the fk to the new identity and enable not null again.
This should work, though maybe there is a better way
CREATE TABLE mytable
(
id int IDENTITY(1,1) primary key,
name varchar(50) not null,
parentid int not null
)
go
alter table mytable
add constraint FK_mytable_parentid FOREIGN KEY ( parentid ) references mytable(id)
ALTER TABLE mytable alter column parentid int null;
insert into mytable(name)
select 'test'
update mytable
set parentid = SCOPE_IDENTITY()
where id = SCOPE_IDENTITY()
ALTER TABLE mytable alter column parentid int not null;
select * from mytable
drop table mytable
From what I understood you already have id before inserting and you can't insert it because identity field isn't letting you to.
Like you mentioned in your comment:
in 1 table I have the rows 34 'name1'
34, 35 'name2' 34 (id,name,parentid)
and I want to copy them to other table
First table
create table Table1
(
id int not null primary key,
name varchar(20) not null,
parentId int not null
)
insert Table1
values
(34, 'name1', 34),
(35, 'name2', 34)
Second table:
create table Table2
(
id int identity(1, 1) primary key,
name varchar(20) not null,
parentId int not null foreign key references Table2(id)
)
Copying data from the first table to the second one:
set identity_insert Table2 on
insert Table2(id, name, parentId)
select *
from Table1
set identity_insert Table2 on
[Update]
If the second table already has values then:
alter table Table2
add oldId int null
alter table Table2
alter column parentId int null
go
insert Table2(name, oldId)
select name, id
from Table1
update tt3
set parentId = tt2.id
from Table2 tt3
join Table1 tt1 on
tt1.id = tt3.oldId
join Table2 tt2 on
tt1.parentId = tt2.oldId
alter table Table2
drop column oldId
alter table Table2
alter column parentId int not null
Don't reference an IDENTITY column as a self-referencing foreign key. Use an alternative key of the table instead. I guess you are using IDENTITY as a surrogate key but every table ought to have a natural key as well, so the IDENTITY column shouldn't be the only key of your table.