I have 3 tables (simplified for the question) like the following:
Candidate
- ID (primary key auto-numbered)
- ProcessedOn
- CandidateState
CandidateData
- CandidateID
- FieldKey
- FieldValue
CandidateTransform
- ID (primary key auto-numbered)
- FieldKey
- FieldValue
CandidateTransform contains data that is used to populate CandidateData (i.e., FieldKey -> FieldKey and FieldValue -> FieldValue). FieldValue in CandeidateTransform contains a string of SQL that selects the FieldValue from another database/table (personnel information) like so:
(SELECT firstName FROM Personnel WHERE id = #CandidateID)
I was able to do this previously as we would only process one candidate at a time and we would create a stored procedure with the INSERT statement like this:
DECLARE #NewCandidate TABLE (id bigint);
INSERT CloudRecruiting.Candidate (CandidateState, ActHdrKey, ActionProcessedOn, JobPostingID, Deleted)
OUTPUT INSERTED.CandidateKey INTO #NewCandidate
VALUES (0, 0, GETDATE(), #PlacementID, 0);
DECLARE #ID bigint;
SET #ID = (SELECT id FROM #NewCandidate);
DECLARE #CandidateID bigint;
SET #CandidateID = (SELECT candidate FROM Personnel WHERE id = #PlacementID);
INSERT INTO .CandidateData (CandidateKey, FieldKey, FieldValue)
VALUES (#ID, 1128, (SELECT ISNULL(LEFT(CONVERT(VARCHAR, dateOfBirth, 120), 10), '') FROM Personnel WHERE id = #CandidateID))
, (#ID, 1159, (SELECT email FROM Personnel WHERE id = #CandidateID))
, (#ID, 1284, (SELECT ssn FROM Personnel WHERE id = #CandidateID))
, (#ID, 1303, CAST((SELECT payRate FROM Personnel WHERE id = #PlacementID) as nvarchar(MAX)))
, (#ID, 1169, (SELECT firstName FROM Personnel WHERE id = #CandidateID))
, (#ID, 1229, (SELECT middleName FROM Personnel WHERE id = #CandidateID))
, (#ID, 1219, (SELECT lastName FROM Personnel WHERE id = #CandidateID))
(etc.)
Now we want to make the process more robust by storing the SELECT statements in a table (CandidateTransform) that the customer can modify and be able to process multiple candidates in one batch via a stored procedure like this (we currently have SQL that will capture the CandidateIDs that we need to process and stores them in a tempTable) :
for each (candidateID in tempTable)
create entry in Candidate
capture new id number from Candidate
for each (sql in CandidateTransform)
execute sql script
store value in CandidateData with the new id, FieldKey and FieldValue
end
end
I have not used the while feature of SQL and not sure if this is the best approach or if there is some other SQL that I can use to do what I need. Any solution needs to support SQL 2008 or later.
Related
I have a table Person:
CREATE TABLE Person
(
ID INT PRIMARY KEY,
FirstName varchar(50),
LastName varchar(50),
Phone varchar(50),
Address varchar(50),
City varchar(50),
PinCode varchar(50),
DateOfBirth DATETIME,
UpdatedOn DATETIME,
UpdatedBy varchar(50)
)
Whenever I insert or update the multiple fields from above table then I want previous value and current value of all updated fields and store that in another table using Trigger. How we can get values of all updated fields.
For example
INSERT INTO Person
VALUES (1, 'first', 'last', '11111', 'add', 'city', 'pin', GETDATE(), GETDATE(), 'ABC')
UPDATE Person
SET FirstName = 'First11',
LastName = 'Last22',
Phone = '1010101010'
WHERE id = 1
When I will hit above commands in both cases I want old and current value and store it in another table. How we can achieve this using triggers?
For SQL Server when you updated ( he delete the old values and then insert the new values )
ALTER TRIGGER [dbo].[YOUR_TRIGGER_NAME_INSERT]
ON [dbo].[YOUR_TABLE]
AFTER INSERT --Here when you insered rows
AS
BEGIN
select * from inserted --get all inserted rows
--Your code
END
And Update
ALTER TRIGGER [dbo].[YOUR_TRIGGER_NAME_UPDATE]
ON [dbo].[YOUR_TABLE]
AFTER UPDATE--Here when you updated rows
AS
BEGIN
select * from inserted --get all inserted (new values) rows
select * from deleted--get all deleted (old values) rows
--Your code
END
This my table1:
Name Description
john student
dom teacher
I need to use SELECT * INTO to transfer it to another table (table2) but I want it with a new column named Auto which is auto-incremented.
Which will look like this:
Name Description Auto
John Student 1
Dom Teacher 2
Current Code: SELECT * INTO table2 FROM table1
Use ROW_NUMBER to add sequential number starting from 1.
SELECT *,
Auto = ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
INTO table2
FROM table1
The accepted answer has additional convenience when breaking one table into several smaller ones with the exact number of rows. If necessary, it is possible to remove the column used for autoincrement.
SELECT *,
ID = ROW_NUMBER() OVER(ORDER BY ( SELECT NULL ))
INTO #Table2
FROM Table1
DECLARE #start INT, #end INT;
SET #start = 1;
SET #end = 5000000;
SELECT *
INTO Table3
FROM #Table2
WHERE ID BETWEEN #start AND #end;
ALTER TABLE Table3 DROP COLUMN ID;
You can use an identity field for this, that's what they're for. The logic of the identity(1,1) means that it will start at the number 1 and increment by 1 each time.
Sample data;
CREATE TABLE #OriginalData (Name varchar(4), Description varchar(7))
INSERT INTO #OriginalData (Name, Description)
VALUES
('John','student')
,('Dom','teacher')
Make a new table and insert the data into it;
CREATE TABLE #NewTable (Name varchar(4), Description varchar(7), Auto int identity(1,1))
INSERT INTO #NewTable (Name, Description)
SELECT
Name
,Description
FROM #OriginalData
Gives the results as;
Name Description Auto
John student 1
Dom teacher 2
If you ran the insert a couple more times your results would look like this;
Name Description Auto
John student 1
Dom teacher 2
John student 3
Dom teacher 4
John student 5
Dom teacher 6
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'm inserting new records into a Person table, and if there's already a record with the same SSN, I want to backup this old record to another table (let's call it PersonsBackup) and update the row with my new values. There is an identity column in Person table that serves as my primary key, which has to be the same.
Source table structure:
Name | Addr | SSN
Person table structure:
PrimaryKeyID | Name | Addr | SSN
PersonBackup table structure:
BackupKeyID | Name | Addr | SSN | OriginalPrimaryKeyID
where OriginalPrimaryKeyID = PrimaryKeyID for the record that was backed up. How can this be done? I was thinking of using cursor to check if SSN matches, then insert that record accordingly, but I've been told that using cursors like this is very inefficient. Thanks for your help!
You can do so like this, combine the insert/update using MERGE
INSERT INTO PersonBackup
SELECT P.Name, P.Addr, P.SSN, P.PrimaryKeyID
FROM Person P
INNER JOIN source s ON P.SSD = s.SSD
MERGE Person AS target
USING (SELECT Name, Addr, SSN FROM SOURCE) AS source (NAME, Addr, SSN)
ON (target.SSN = source.SSN)
WHEN MATCHED THEN
UPDATE SET name = source.name, Addr = source.Addr
WHEN NOT MATCHED THEN
INSERT(Name, Addr, SSN)
VALUES(source.name, source.addr, source.SSN)
Here is some pseudocode to get you started:
Insert into PersonBackup table all Person data where SSN joins to Source data
Insert into Person table all Source data where SSN doesn't join to Person data
Update Person table all Source data where SSN joins to Person Data
So some hints:
Figure out a query that returns Person data where SSN is in the Source data.
Figure out a query that returns Source Data where SSN isn't in Person data.
Check out the syntax for INSERT INTO.
Check out the syntax for UPDATE FROM.
Assuming that BackupKeyID is identity in the PersonBackup table, you may try update statement with the output clause followed by insert of the records not existing in the target table:
update p
set p.Name = s.Name, p.Addr = s.Addr
output deleted.Name, deleted.Addr,
deleted.SSN, deleted.PrimaryKeyID into PersonBackup
from Source s
join Person p on p.SSN = s.SSN;
insert into Person (Name, Addr, SSN)
select s.Name, s.Addr, s.SSN
from Source s
where not exists (select 1 from Person where SSN = s.SSN);
or using insert into ... from (merge ... output) construct in a single statement:
insert into PersonBackup
select Name, Addr, SSN, PrimaryKeyID
from
(
merge Person p
using (select Name, Addr, SSN from Source) s
on p.SSN = s.SSN
when matched then
update set p.Name = s.Name, p.Addr = s.Addr
when not matched then
insert (Name, Addr, SSN) values (s.Name, s.Addr, s.SSN)
output $action, deleted.Name, deleted.Addr, deleted.SSN, deleted.PrimaryKeyID)
as U(Action, Name, Addr, SSN, PrimaryKeyID)
where U.Action = 'UPDATE';
DECLARE #TEMP TABLE (SSN VARCHAR(100),Name VARCHAR(MAX),Addr VARCHAR(MAX) ),
#SSN VARCHAR(100),
#Name VARCHAR(MAX),
#ADDR VARCHAR(MAX)
//INSERT YOUR VALUES INTO THIS TEMP VARIABLE FIRST.
SET #SSN = (SELECT SSN FROM #TEMP)
SET #Name = (SELECT NAME FROM #TEMP)
SET #Addr = (SELECT ADDR FROM #TEMP)
IF EXISTS (SELECT 1 FROM Person_table WHERE SSN = #SSN)
BEGIN
//BACKUP FIRST
INSERT INTO PersonBackup_table
SELECT * FROM Person_table WHERE SSN = #SSN
//UPDATE NEXT
UPDATE A
SET A.NAME = #NAME,
A.ADDR = #ADDR
FROM Person_table A
WHERE A.SSN = #SSN
END
ELSE
BEGIN
INSERT INTO Person_table VALUES #Name,#Addr,#SSN
END
What is the simplest way to insert a Car for each user in Users and set the users CarID to the ID of the inserted Car?
[Users]
- ID
- Name
- CarID
[Cars]
- ID (Auto increment)
- Name
Sorry if this might be a duplicate question but I can't find any simple solutions. Everything I've found is using complicated cursors, pointers etc.
A simple and reusable syntax for this would save me hours while migrating data during system upgrades etc.
If you are on SQL Server 2008 or later you can use merge and output something like this.
Sample tables and data:
declare #Users table
(
ID int identity primary key,
Name varchar(10) not null,
CarID int null
);
declare #Cars table
(
ID int identity primary key,
Name varchar(10) not null
);
insert into #Users(Name) values ('User1'),('User2'),('User3');
Add one care for each user and move the auto-generated CarID back to Users.
declare #ID table(CarID int, UserID int)
merge #Cars as C
using #Users as U
on 0 = 1
when not matched then
insert (Name) values ('CarName')
output inserted.ID, U.ID into #ID;
update U
set CarID = I.CarID
from #Users as U
inner join #ID as I
on U.ID = I.UserID
Try it out on SE Data.
More info on the merge/output trick can be found here.
I'm assuming the code you're using lets you call stored procedures?
create procedure dbo.CarUserInsert_sp
(
#p_UserID int,
#p_UserName varchar(100),
#p_CarID int,
#p_CarName varchar(100)
)
as
if not exists ( select 1 from Cars where ID = #p_CarID )
insert Cars values ( #p_CarID, #p_CarName )
if exists (select 1 from Users where ID = #p_UserID )
update Users set Name = #p_UserName, CarID = #p_CarID where ID = #p_UserID
else
insert Users values ( #p_UserID, #p_UserName, #p_CarID )
go
try this:
insert into cars (name)
select distinct(name) from users
update user
set carId = (select ID from cars where cars.name=user.Name)