How to insert data into 3 related tables (SQL Server)
For example, I have tables Customer <-- Customer_Address --> Address
After I insert data into Customer and Address, how do I insert the IDs from Customer and Address in to Customer_Address? (join table)
Thanks!
Use SCOPE_IDENTITY, ##IDENTITY can return a value from any scope:
DECLARE #CustomerId INT
DECLARE #AddressId INT
BEGIN TRANSACTION
INSERT INTO CUSTOMER (blah, blah) values (blah, blah)
SET #CustomerId = SCOPE_IDENTITY
INSERT INTO ADDRESS (blah, blah) values (blah, blah)
SET #AddressId = SCOPE_IDENTITY
INSERT INTO CUSTOMERADDRESS (CustomerId,AddressId) values (#CustomerId,#AddressId)
COMMIT TRANSACTION
If you want to insert more than one row you can use the output clause:
declare #insertedAddresses table (OriginalID int, AddressID int);
declare #insertedCustomers table (OriginalID int, CustomerID int);
insert into dbo.Addresses (AddressData)
output source.OriginalID, inserted.AddressID into #insertedAddresses
select AddressData from source;
insert into dbo.Customers (CustomerData)
output source.OriginalID, inserted.CustomerID into #insertedCustomers
select CustomerData from source;
insert into dbo.Customer_Address (AddressID, CustomerID)
select a.AddressID, c.CustomerID
from #insertedAddresses a inner join #insertedCustomers c on c.OriginalID=a.OriginalID;
If the IDs from the Customer and Address tables are Identity columns, you can store the new ID into a variable.
DECLARE #CustomerID int
SELECT #CustomerID = ##IDENTITY FROM TABLE CUSTOMER
Similar syntax could be used for the Address table. Then in your INSERT statement you can do this:
INSERT INTO Customer_Address (CustomerID, AddressID)
VALUES (#CustomerID, #AddressID)
use a transaction, and remember the identities. in sql server -
declare #CustomerId int
declare #AddressId int
begin tran
insert into Customer (blah, blah) values (blah, blah)
set #CustomerId = ##IDENTITY --assuming there are no triggers
insert into [Address] (blah, blah) values (blah, blah)
set #AddressId = ##IDENTITY --once again, no triggers to mess up the ##IDENTITY
insert into CustomerAddress(CustomerId,AddressId) values (#CustomerId,#AddressId)
commit
Here is one way.
Declare CustomerID int, AddressID int
insert into Customer (list, of, fields) values (list, of, values)
select #CustomerID=scope_Identity()
insert into Address (list, of, fields) values (list, of, values)
select #AddressID =scope_Identity()
insert into Customer_Address (CustomerID, AddressID) values (#CustomerID, #AddressID)
If it is for an application, why don't just use Linq, you can accomplish things like this in just a few minutes, in a more compressive way and more thigh to the business logic model.
If not the I highly recommend the transaction and SCOPE_IDENTITY version, because if you are in a heavy loaded system, an other operation can do an insert and change the identity value so you will end with inconsistent data.
Related
I am trying to read data from 2 tables and inserting to similar tables in a different database.
Here is my query
BEGIN TRANSACTION
Delete from aspnet_Website.[dbo].[Places]
DECLARE #AutoID int;
insert into aspnet_Website.[dbo].[Places] (ReferenceID, Name, OtherKnownNames, Description, Email)
select ReferenceID, Name, OtherKnownNames, Description, Email from DB_A290D0_places.
[dbo].[places]
where PublishingStatus=0
SELECT #AutoID = scope_identity();
insert into aspnet_Website.[dbo].[Schedules] (Timing, Type, PlaceID)
select [Timing], [Type], #AutoID from DB_A290D0_places.[dbo].[Schedules]
COMMIT
I am getting error
Incorrect syntax near '#AutoID'.
and even I am not sure that it will work or not.
'Places' table has ID field which is auto id and it is used as FK in Schedule table, so for every 'place' row I need to get auto id and insert it into the schedule table along with the corresponding table data from another database.
Update1 : I have fixed the syntax error, I can see records added into the table but last generated AutoID is being used for all rows in child table. I want to use autoid generated for each row.
Update2 : following script worked
BEGIN TRANSACTION
Delete from aspnet_Website.[dbo].[Places]
declare #NewId table (ID int);
insert into aspnet_Website.[dbo].[Places] (ReferenceID, Name, OtherKnownNames, Description, Email)
select ReferenceID, Name, OtherKnownNames, Description, Email from DB_A290D0_places.
[dbo].[places]
where PublishingStatus=0
output Inserted.ID into #NewId (ID)
insert into aspnet_Website.[dbo].[Schedules] (Timing, [Type], PlaceID)
select [Timing], [Type], P.ID
from DB_A290D0_places.[dbo].[Schedules] S
inner join #NewId P on P.ID = S.PlaceId;
COMMIT
You can't store more than one value in a variable, the the line SELECT #AutoID = scope_identity(); will only capture the last id inserted.
To solve you problem have you considered not changing the ids by setting IDENTITY_INSERT ON and inserting the original ids?
Otherwise use the OUTPUT clause to capture the new ids, map them to the old ids, and then insert them into the Schedules table.
begin transaction
delete from aspnet_Website.[dbo].[Places];
declare #AutoID int;
declare #NewId table (ID int, OldID int);
-- In an insert statement you can't access the source table in the output clause unfortunately
/*
insert into aspnet_Website.[dbo].[Places] (ReferenceID, [Name], OtherKnownNames, [Description], Email)
output Inserted.ID, P.ID into #NewId (ID, OldID)
select ReferenceID, [Name], OtherKnownNames, [Description], Email
from DB_A290D0_places.[dbo].[places] P
where PublishingStatus = 0;
*/
-- However in a merge statement you can access both the source and destination tables in the output clause.
merge into aspnet_Website.[dbo].[Places] T
using DB_A290D0_places.[dbo].[places] S on 1 = 0 -- always false
when not matched by target and S.PublishingStatus = 0 then -- happens for every row, because 1 is never 0
insert (ReferenceID, [Name], OtherKnownNames, [Description], Email)
values (S.ReferenceID, S.[Name], S.OtherKnownNames, S.[Description], S.Email)
output Inserted.ID, S.ID into into #NewId (ID, OldID);
insert into aspnet_Website.[dbo].[Schedules] (Timing, [Type], PlaceID)
select [Timing], [Type], P.ID
from DB_A290D0_places.[dbo].[Schedules] S
inner join #NewId P on P.OldId = S.PlaceId;
commit
I have an old table with lots of columns that i want to split into 3 tables with many to many relation.
The old table have no identity column.
Old table:
CustomerNumber
FirstName
LastName
Address
Postal
City
....
New tables
Customer:
Id
Customernumber
Firstname
Lastname
... ect
Address:
Id
Address
Postal
City
... ect
CustomerAddress
Id
CustomerId
AddressId
Now how can I spilt the old tabel into the new ones using SQL?
I have tried with MERGE but that can't handle more than one table at the time. One option is using CURSOR, but I read that it is a bad idea, to use that or iteration, but for now that is the only solution I have found for this.
declare
#CustomerId bigint,
#CustomerNumber float,
#Status int,
#Address varchar(50),
#RoadNumber int,
#LastEdited datetime,
#AddressId bigint
declare my_cursor cursor
local static read_only forward_only
for
select CustomerNumber, Address, Housenumber, Status, Date
from [db1].dbo.OldCustomer k
where
FIRMANR in (1, 40, 60, 80, 90, 120, 180, 400)
open my_cursor
fetch next from my_cursor into #CustomerNumber, #Address, #RoadNumber, #Status, #LastEdited
while ##FETCH_STATUS = 0
begin
--if the customer already exists we get the identity
if exists (select Id from [db2].dbo.Customers where CustomerNumber = #CustomerNumber)
select #CustomerId = Id from [db2].dbo.Customers where CustomerNumber = #CustomerNumber
--if the customer does not exit we need to insert and retrieve the new Identity value
else
begin
-- insert the customer
insert into [db2].dbo.Customers (CustomerNumber, [Status], LastEdited) values (#CustomerNumber, #Status, #LastEdited)
set #CustomerId = SCOPE_IDENTITY()
end
-- get address if it already exists
if exists (select Id from [db2].dbo.Addresses where Road = #Address and Roadnumber = #RoadNumber)
select #AddressId = Id from [db2].dbo.Addresses where Road = #Address and Roadnumber = #RoadNumber
else
begin
-- insert new addresses
insert into [db2].dbo.Addresses (Road,Roadnumber) values (#Address, #RoadNumber)
set #AddressId = SCOPE_IDENTITY()
end
-- insert customer => address reference if it does not exist
if not exists (select Id from [db2].dbo.CustomerAddress where CustomerId = #CustomerId and AddressId = #AddressId)
-- insert customer => address reference
insert into [db2].dbo.CustomerAddress(CustomerId,AddressId) values (#CustomerId, #AddressId)
fetch next from my_cursor into #CustomerNumber, #Address, #RoadNumber, #Status, #LastEdited
end
close my_cursor
deallocate my_cursor
It would probably be easiest to:
Add a column to old customer called AddressId
Populate it with unique IDs (you can reuse the Customer ID, or if its GUID, use NewID())
Create new tables for address and customeraddress
Insert part of the old customer data into each new table
Drop columns from Customer that relate to address
--populate new Address table
INSERT INTO Address(id,col1,col2...)
SELECT AddressID, col1, col2... FROM Customer
--populate new CustomerAddress table
INSERT INTO CustomerAddress(CustomerId,AddressId)
SELECT Id, AddressID FROM Customer
With SQLS you can do this as a simple script, with a transaction if you want.. No need for stored procedures, cursors, merge etc..
Don't give CustomerAddress its own Id column; the primary key of CustomerAddress is the combination of CustomerId and AddressId; make a composite PK, not a separate one
Tbh, I'd probably not have a CustomerAddress table and instead just have a BillingAddressId, WorkAddressId, HomeAddressId, ShippingAddressId column in customer, but it's up to you how to manage this; If you have lots and variable types of addresses then sure, have a M:M breakdown, but if realistically your customers are only ever going to have up to 3 addresses etc, I'd stick with having a named column stating what the address was for, in customer
If you do go with a CustomerAddress table, consider adding a column declaring the type/reason for the address
I have two tables
First table is CUSTOMERS with columns CustomerId, CustomerName
Second table is LICENSES with columns LicenseId, Customer
The column Customer in the second table is the CustomerId from the First table
I wanted to create a stored procedure that insert values into table 2
Insert into Licenses (Customer)
Values(CustomerId)
How can I get this data from the other table?
Thanks in advance for any help
this looks to me like simply a syntax question - I think what you want is
INSERT INTO Licenses (Customer) SELECT CustomerId FROM customers where ...
CREATE PROCEDURE uspInsertToTable
#CustomerID INT OUTPUT,
#CustomerName VARCHAR(50),
#LicenseID INT,
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO CUSTOMERS
VALUES (#CustomerName);
SET #CustomerID=SCOPE_IDENTITY();
INSERT INTO LICENSES
VALUES (#LicenseID, #CustomerID)
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION;
END
END CATCH;
END;
];
if CustomerID is identity, I wish that it will be work... :)
If you're using SQL Server and your PK is autocorrelative using identity(1,1) do this:
CREATE PROC Register #CustomerName varchar(25)
AS BEGIN
INSERT INTO CUSTOMERS (Customer) VALUES (#CustomerName)
INSERT INTO LICENSES (SELECT MAX(CustomerId) FROM CUSTOMERS)
END
But if you don't want to use identity (1,1) do this:
CREATE PROC Register #CustomerName varchar(25)
AS BEGIN
DECLARE #idc int = SELECT MAX(CustomerId) FROM CUSTOMERS;
DECLARE #idl int = SELECT MAX(LicenseId) FROM LICENSES ;
INSERT INTO CUSTOMERS (CustomerId, CustomerName) VALUES (#idc,#CustomerName)
INSERT INTO LICENSES (LicenseId, Customer) VALUES (#idl,#idc)--I guess your field Customer is the foreign key
END
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.
Just as a preface, I'm not very knowledgeable on T-SQL syntax.
I'd like to create a simple SQL script that will make 3 insert statements.
Insert A
Insert B
Insert C
Insert A statement's identity or "ID" would be needed in insert B statement.
And both the identities Inserts A & B would be needed in Insert C statement.
Pseudo code would look something like:
INSERT INTO tableA
VALUES ('blah', 'blah')
INSERT INTO tableB
VALUES (IDENTITY_FROM_A_INSERT, 'foo')
INSERT INTO tableC
VALUES (IDENTITY_FROM_A_INSERT, IDENTITY_FROM_B_INSERT)
How would I go about writing this script?
Use SCOPE_IDENTITY() after each insert in order to get the identity of the inserted row (in the current session).
I have used two variables to capture the two identities and then insert them into the third table:
DECLARE #Id1 INT
DECLARE #Id2 INT
INSERT INTO tableA VALUES ('blah', 'blah')
SET #Id1 = SELECT SCOPE_IDENTITY()
INSERT INTO tableB VALUES (IDENTITY_FROM_A_INSERT, 'foo')
SET #Id2 = SELECT SCOPE_IDENTITY()
INSERT INTO tableC VALUES (#Id1, #Id2)
scope_identity() is perfect for integer identifiers on single-record insertions (+1 to the other answer btw). However, if you find yourself using a guid/uniqueidentifier (newsequentialid(), newid(), etc) or inserting multiple records at once, you'll need something a little different:
declare #id uniqueidentifier;
-- Table variable for storing your newly inserted identifiers:
declare #NewlyInsertedIds table
(
[Id] uniqueidentifier
);
insert [MyTable]
(
[Blah1]
,[Blah2]
)
-- in the output clause you can access the inserted/deleted pseudo tables:
ouptut inserted.[Id]
into #NewlyInsertedIDs
(
[Id]
)
values
(
'Blah'
,'Blah'
);
select
#id = [Id]
from #NewlyInsertedIds;
Check out the OUTPUT Clause for more information.