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
Related
I have three tables. table1 for insert data and table2, table3 for selecting data.
table1:
int id,
varchar name,
varchar gender,
varchar category,
table2:
int id,
varchar gender,
table3:
int id,
varchar category,
According to these tables, i'm using stored procedure as below:
Create procedure ABC
(
#id varchar(10),
#name varchar(11),
#gender varchar(10),
#category varchar(10)
)
As
begin
Insert into table1(id, name, gender, category) select #id, #name, g.id, c.id from gender g, category c where g.gender=#gender and c.category=#category
end
Now, using this if gender or category is empty then data is not inserting. Please tell me where i'm wrong and tell best query to insert data using select query. Please help me to solve the problem. Thanks
You are using and in where clause which means only if gender = #gender then it is selecting any record.
Create procedure ABC
(
#id int, ----- change this to int since id is int type in your table
#name varchar(11),
#gender varchar(10)='',
#category varchar(10)=''
)
As
begin
Insert into table1(id, name, gender, category)
select #id, #name, g.id, c.id from gender g, category c
where (g.gender=#gender or isnull(#gender,'')='') and (c.category=#category or isnull(#category,'')='')
end
Although you may use this approach. But since you want to insert id of gender and category column in your table, then on passing null or blank value what is your expected id to be put in your record.
It is better if you first check gender and category value in your respected table and if record not found then insert it in your table and then save its corresponding id in your final table.
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.
Destination tables look like this:
Source table look like this:
Customer
CustomerId FirstName LastName Email Address1 Address2 City Zip
Person table in destination is a base table (which will later be inherited by new customer table). So I am trying to export a row from one table and populate 3 tables in destination.
I managed to do this in following way:
Get records from source table (Customer)
Create empty AddressId field
Populate Address table using OLE DB Command task (it calls stored procedure which returns SCOPE_IDENTITY() that's mapped to AddressId field)
Repeat step 3 for populating Person table (and retrieving PersonId
Populate cross reference table PersonAddress using PersonId and AddressId fields
Screenshot of this package is below.
Biggest issue with this approach is that OLE DB Command task is inserting row by row and it makes the whole package extremely slow. Is it possible to achieve the same thing but using fast load?
I am able to do it using OLE DB Command task which calls the stored procedure and then
I don't think you need SSIS.
You can use OUTPUT clause of INSERT which returns all identity keys to a temporary table
Lets try reproduce your scenario...
set nocount on
go
create table Customer (CustomerId int, CustomerName varchar(100) null, Address1 varchar(100) null, Address2 varchar(100) )
create table [Person] (PersonId int identity, PersonName varchar(100) null)
create table [Address] (AddressId int identity, AddressLine varchar(100) null)
create table [PersonAddress] (AddressId int, PersonId int )
go
-- create some data...
insert into Customer (CustomerId) values ( 1000000 + convert(int, RAND() * 1000000) )
go 1000
update Customer
set CustomerName = 'CustomerName ' + convert(varchar, CustomerId),
Address1 = 'Address1 ' + convert(varchar, CustomerId),
Address2 = 'Address2 ' + convert(varchar, CustomerId)
go
declare #identities_Person table ([rownumber] int identity, id int)
declare #identities_Address table ([rownumber] int identity, id int)
insert into Person (PersonName)
output inserted.PersonId into #identities_Person
select
c.CustomerName
from Customer c
order by c.CustomerId
insert into [Address] (AddressLine)
output inserted.AddressId into #identities_Address
select
c.Address1
from Customer c
order by c.CustomerId
insert into [PersonAddress] (PersonId, AddressId)
select p.id, a.id
from #identities_Address a
inner join #identities_Person p on p.rownumber = a.rownumber
select *
from PersonAddress pa
inner join [Address] a on a.AddressId = pa.AddressId
inner join [Person] p on p.PersonId = pa.PersonId
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
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.