I am using SQL Server 2014. I looked up solution by Joel Coehoorn for this question but it did not work for me.
I have a table for clients which I renamed to Aac_Client which has both client information and address information as columns. I want to move Address to a new table Address to organise things and keep other remaining things at Ac_Client. I am trying to do the following:
Note the relationship between Address and Client is One to One.
BEGIN TRANSACTION
DECLARE #DataID int;
-- Insert Address
INSERT INTO Address ([StreetNumber],[StreetName] ,[StreetAddress2] ,[Unit] ,[City] ,[State] ,[Zip] ,[County])
SELECT [StreetNumber], [StreetName], [StreetAddress2], [Unit] ,[City] ,[State] ,[Zip] ,
NULL AS [County]
FROM Aac_Client
-- Get Address Id
SELECT #DataID = scope_identity();
-- Insert Client
INSERT INTO Ac_Client ( AddressId, Name, Phone, Contact )
SELECT #DataID AS AddressId, Name , Phone, Contact FROM Aac_Client
COMMIT
But the problem is INSERT is carried at once and I get the last ID of address in SELECT #DataID = scope_identity();
BEGIN TRANSACTION
IF OBJECT_ID('tempdb..#InsertedAddresses') IS NOT NULL
BEGIN
DROP TABLE #InsertedAddresses
END
CREATE TABLE #InsertedAddresses (
AddressId INT
,ClientId INT
)
DECLARE #DataID int;
-- add a unique client identifier
-- Insert Address
INSERT INTO Address ([StreetNumber],[StreetName] ,[StreetAddress2] ,[Unit] ,[City] ,[State] ,[Zip] ,[County], [ClientId])
OUTPUT INSERTED.AddressId, INSERTED.ClientId (AddressId, ClientId)
SELECT [StreetNumber], [StreetName], [StreetAddress2], [Unit] ,[City] ,[State] ,[Zip] ,
NULL AS [County]
FROM Aac_Client
-- Insert Client
INSERT INTO Ac_Client ( AddressId, Name, Phone, Contact, ClientId)
SELECT i.AddressId, Name , Phone, Contact, c.ClientId
FROM Aac_Client c
INNER JON #InsertedAddresses i
ON c.ClientId = i.ClientId
COMMIT
use the output clause of the first insert statement into a temp table. Also is it possible that clients will have more than one address? Or more then 1 client at the same address? If so you might want to consider putting the AC_Client ID in the address table instead of the address_id in the client table. Also if you are not worried about changing addresses independently in the case of multiple clients at same address you could consider a 3rd table to relate addresses and clients so you don't have to repeat addresses or clients to create a many to many relationship.
I guess to expand scope_identity is a scalar value meaning only 1 value is held and it is the identity of the last row altered in the scope. In your case you need all of the ids so you need to use the output clause.
--Add the following Columns that you can later Drop
ALTER TABLE [Address] ADD Col_GUID UNIQUEIDENTIFIER;
ALTER TABLE [Aac_Client] ADD Col_GUID UNIQUEIDENTIFIER;
GO
-- Give a Unique value to each row.
UPDATE [Aac_Client] SET Col_GUID = NEWID();
BEGIN TRANSACTION;
-- Table variable to capture newly generated Identity values
Declare #NewID TABLE (AddressId INT, Col_GUID UNIQUEIDENTIFIER)
-- Insert Address (with output clause to get the Identity values)
INSERT INTO [Address] ([StreetNumber],[StreetName] ,[StreetAddress2]
,[Unit] ,[City] ,[State] ,[Zip] ,[County], Col_GUID)
OUTPUT Inserted.AddressId , Inserted.Col_GUID INTO #NewID (AddressId , Col_GUID)
SELECT [StreetNumber], [StreetName], [StreetAddress2]
,[Unit] ,[City] ,[State] ,[Zip] , NULL AS [County] , Col_GUID
FROM Aac_Client
-- Insert Client joining with the Table variable on the guid column to
-- to get the new Identity values.
INSERT INTO Ac_Client ( AddressId, Name, Phone, Contact )
SELECT N.AddressId, a.Name , a.Phone, a.Contact
FROM Aac_Client a
INNER JOIN #NewID N ON n.Col_GUID = a.Col_GUID
COMMIT TRANSACTION;
Finally you can drop the Columns you added for this purpose.
ALTER TABLE [Address] DROP COLUMN Col_GUID;
ALTER TABLE [Aac_Client] DROP COLUMN Col_GUID;
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 an Excel file that I imported into a table (with customerID, Customer name, accountID, Address, and accountmanagerID) in my SQL Server database.
Now I want to split the data into different tables, for customers, accounts, address, and managers.
The issue is that some customers have more then 5 accounts with different managers others with 2 different account with the same manager.
If I use the stored procedure shown here, I can only store one customer with one account.
But what I want to do is to store one customer with all the different accounts related to him.
This is my stored procedure:
ALTER PROCEDURE [dbo].[sp_Import_Client]
AS
BEGIN
TRUNCATE TABLE Customers
TRUNCATE TABLE Addresses
TRUNCATE TABLE Accounts
TRUNCATE TABLE Managers
DECLARE NewClient CURSOR LOCAL FAST_FORWARD READ_ONLY
FOR
SELECT
Distinct(CustomerName), --CustomerName
AccountID , --AccountNo
Address,
ManagerName -- manager
FROM
OldDetails
DECLARE #CustomerName VARCHAR(255)
DECLARE #Contact VARCHAR(300)
DECLARE #AccountNo VARCHAR(20)
DECLARE #Address1 VARCHAR(300)
DECLARE #CustomerID INT
OPEN NewClient
FETCH FROM NewClient INTO #CustomerName, #Manager, #AccountNo, #Address1
WHILE (##FETCH_STATUS = 0)
BEGIN
--- 1 Insert Customer
INSERT INTO Customers (CustomerName)
VALUES (#CustomerName)
---Set the CustomerID to continue with stage 2
SET #CustomerID = SCOPE_IDENTITY()
--- 2 Insert CustomerAddresses
INSERT INTO Address (CustomerID, Address1)
VALUES (#CustomerID, #Address1)
---3 Insert Managers
INSERT INTO Managers (CustomerID, Name)
VALUES (#CustomerID, #Contact)
---4 Insert Account
INSERT INTO Accounts(CustomerID, AccountNo, Manager)
VALUES (#CustomerID, #AccountNo, #manager)
FETCH NEXT FROM NewClient INTO #CustomerName, #Manager, #AccountNo, #Address1
END
CLOSE NewClient
DEALLOCATE NewClient
END
I think you might need to change the step 1 in your procedure to check if the customer already exists, and if it does, use it instead of inserting a new row for it. You can use the code block below instead of your INSERT statement:
-- SET THE #CustomerID
SET #CustomerID = NULL
SELECT #CustomerID = ID
FROM Customers
WHERE CustomerName = #CustomerName
IF #CustomerID IS NULL
BEGIN
--- 1 Insert Customer
INSERT INTO Customers (CustomerName)
VALUES (#CustomerName)
---Set the CustomerID to continue with stage 2
SET #CustomerID = SCOPE_IDENTITY()
END
This way, you can save only 1 customer record, and multiple rows for managers / accounts if needed.
Hope it makes sense.
EDIT
By the way, a small observation, you dont need to specify FAST_FORWARD and READ_ONLY together because FAST_FORWARD implies READ_ONLY and FORWARD_ONLY, so FAST_FORWARD will do it.
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.
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.