Upsert SQL query - sql

I am looking for some advice how to optimize a couple of SQL stored procedures. With the 1st query I am doing insert, and with the 2nd one I am doing update, if the data exists.
What I want to do is merge both stored procedures in one, where the query will check if the data exists than update, else insert a new row.
Here is what I have at this time:
update SP:
ALTER PROCEDURE [dbo].[UpdateStep1](#UserId nvarchar(50), #First_Name nvarchar(50), #Last_Name nvarchar(50),
#TitlePosition nvarchar(30))
AS
BEGIN
UPDATE Company_Information
SET First_Name = #First_Name,
Last_Name = #Last_Name,
Title_Position=#TitlePosition,
WHERE UserId = #UserId
END
insert SP:
ALTER PROCEDURE [dbo].[InsertStep1](#UserId nvarchar(50), #First_Name nvarchar(50), #Last_Name nvarchar(50),
#TitlePosition nvarchar(30))
AS
BEGIN
INSERT INTO Company_Information(UserId,
First_Name,
Last_Name,
Title_Position)
VALUES
(#UserId,
#First_Name,
#Last_Name,
#TitlePosition)
END
So, I would like to merge both SP in one, and the SP to check if there is already data for that UserId than update, else insert a new row.

MERGE Statement?
CREATE PROCEDURE [dbo].[MERGEStep1](#UserId nvarchar(50), #First_Name nvarchar(50), #Last_Name nvarchar(50), #TitlePosition nvarchar(30))
AS
BEGIN
MERGE Company_Information WITH(HOLDLOCK) AS T
USING(SELECT 1 S) S
ON T.UserId = #UserId
WHEN MATCHED THEN UPDATE SET
First_Name = #First_Name,
Last_Name = #Last_Name,
Title_Position=#TitlePosition
WHEN NOT MATCHED THEN
INSERT (UserId, First_Name, Last_Name, Title_Position)
VALUES(#UserId, #First_Name,#Last_Name,#TitlePosition);
END

Follow these steps:
Create a variable to test it (ex: #id)
Select #id = UserId from Company_Information where UserId = #UserId
If #id = #userId update, otherwise insert
As #gbn specified, be aware of concurrence issues.

Related

Create trigger in SQL using inserted table to concatenate data, but end up with two rows

I'm trying to create a trigger when inserting first name and last name (without email), it will automatically generate the email, but it gives me two rows. One is that the email is NULL, and the other is the correct result.
CREATE TRIGGER trg_assignEmail
ON StudentInformation
FOR INSERT
AS
BEGIN
DECLARE
#FirstName NVARCHAR (50),
#LastName NVARCHAR (50),
#Email NVARCHAR (100)
SELECT
#FirstName = inserted.FirstName,
#LastName = inserted.LastName,
#Email = inserted.Email
FROM
inserted
IF NOT EXISTS (SELECT Email FROM inserted)
SET #Email = #FirstName+'.'+#LastName+'#disney.com'
SELECT
#FirstName = inserted.FirstName,
#LastName = inserted.LastName,
#Email = TRIM(#FirstName)+'.'+TRIM(#LastName)+'#disney.com'
FROM
inserted
INSERT INTO StudentInformation
(
FirstName, LastName, Email
)
values (#FirstName, #LastName, #Email)
END
The results
I guess you are looking for something similar to this:
CREATE TRIGGER trg_assignEmail
ON StudentInformation
INSTEAD OF INSERT
AS
BEGIN
DECLARE
#FirstName NVARCHAR (50),
#LastName NVARCHAR (50),
#Email NVARCHAR (100)
SELECT
#FirstName = INSERTED.FirstName,
#LastName = INSERTED.LastName,
#Email = INSERTED.Email
FROM
INSERTED
IF (#Email IS NULL)
BEGIN
SET #Email = TRIM(#FirstName)+'.'+TRIM(#LastName)+'#disney.com'
END
INSERT INTO StudentInformation (FirstName, LastName, Email)
VALUES (#FirstName, #LastName, #Email)
END
When a new record is inserted to StudentInformation, it is required to check whether the email is provided and create a new email address when it's not provided. So INSTEAD OF INSERTis used in my attempt above to do the checking before the INSERT statement.

SQL Server stored procedure with multiple table insert

I am using SQL Server Management Studio 2014 and I am writing a stored procedure to insert into many tables with foreign keys.
I am getting two errors:
Msg 547, Level 16, State 0, Procedure insertintoorders, Line 163
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__Customers__Addre__145C0A3F". The conflict occurred in database "FlowerCompany", table "dbo.Addresses", column 'AddressID'.
Msg 547, Level 16, State 0, Procedure insertintoorders, Line 178
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__Orders__Customer__1FCDBCEB". The conflict occurred in database "FlowerCompany", table "dbo.Customers", column 'CustomerID'.
Here is my stored procedure and my testing execution of it:
CREATE PROCEDURE insertintoorders
#Street varchar(50),
#City varchar(30),
#State varchar(2),
#Zip varchar(9),
#Phone varchar(10),
#FlowerName varchar(50),
#FirstName varchar(40),
#LastName varchar(40),
#OrderStatus varchar(12),
#DeliverDate date,
#OrderMessage varchar(100),
#OrderDate date,
#Vase bit,
#OrderCost decimal(6,2)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #flower int;
SELECT #flower = Arr.FlowerID
FROM Arrangements Arr
WHERE FlowerName = #FlowerName
DECLARE #AddID int;
SELECT #AddID = coalesce((SELECT MAX(AddressID) + 1 FROM Addresses), 1)
DECLARE #PhoneID int;
SELECT #PhoneID = coalesce((SELECT MAX(PhoneID) + 1 FROM Phone), 1)
DECLARE #CustID int;
SELECT #CustID = coalesce((SELECT MAX(CustomerID) + 1 FROM Customers), 1)
DECLARE #Del int;
SELECT #Del = coalesce((SELECT MAX(DeliveryID) + 1 FROM Delivery), 1)
DECLARE #Ords int;
SELECT #Ords = coalesce((select max(StatusID) + 1 from OrderStatus), 1)
DECLARE #Ord int;
SELECT #Ord = coalesce((select max(OrderID) + 1 from Orders), 1)
INSERT INTO Addresses (Street, City, States, Zip)
VALUES (#Street, #City, #State, #Zip)
SET #AddId = SCOPE_IDENTITY()
INSERT INTO Phone (Phone)
VALUES (#Phone)
SET #PhoneID = SCOPE_IDENTITY()
INSERT INTO Customers (AddressID, PhoneID, FirstName, LastName)
VALUES (SCOPE_IDENTITY(), SCOPE_IDENTITY(), #FirstName, #LastName)
SET #CustID = SCOPE_IDENTITY()
INSERT INTO Delivery (DeliverDate)
VALUES (#DeliverDate)
SET #Del = SCOPE_IDENTITY()
INSERT INTO OrderStatus (OrderStatus)
VALUES (#OrderStatus)
SET #Ords = SCOPE_IDENTITY()
INSERT INTO Orders ([CustomerID], [FlowerID], [StatusID],[DeliveryID], OrderMessage, OrderDate, OrderCost, Vase)
VALUES (SCOPE_IDENTITY(), #flower, SCOPE_IDENTITY(), SCOPE_IDENTITY(), #OrderMessage, #OrderDate, #OrderCost, #Vase)
SET #Ord = SCOPE_IDENTITY()
END
GO
EXEC insertintoorders #Street = '555 LANE', #City='Somewhere', #State = 'XX', #Zip = '99999', #Phone = '1234567896', #FlowerName = 'The Flower of Love', #FirstName = 'George',
#LastName = 'Fish', #DeliverDate = '10/10/2016', #OrderStatus = 'Completed', #OrderMessage = 'Fishy flowers', #OrderDate = '03/03/2016', #OrderCost = '200', #Vase = '1'
Although I had read the errors, I am not understanding how to fix them or why they are there.
You are using IDENTITY_INSERT and inserting your own values for PhoneId and AddressId. SCOPE_IDENTITY() is always the last value inserted into an identity column, so after you insert into phone it will return PhoneId and never AddId.
Option 1: Use the values you have. This is a bad idea because if you do two adds at the same time, you might get conflicts
SET IDENTITY_INSERT dbo.Customers ON
INSERT INTO Customers (CustomerID,AddressID,PhoneID,FirstName,LastName)
VALUES (#CustID, #AddId, #PhoneId ,#FirstName, #LastName)
SET IDENTITY_INSERT dbo.Customers OFF
Option 2: Just let the system set the identity values and get them after each insert, i.e.:
INSERT INTO Addresses (Street, City, States, Zip)
VALUES (#Street,#City,#State,#Zip)
SET #AddId = SCOPE_IDENTITY()

How to create procedure with to Keep Track of Last Changed Data

So i have two tables: User and LogUser. When i update table User the old data should be keep for history in LogUser table.
I have created procedure for update, but don't know how to keep history of changed data.
Update Procedure:
CREATE PROCEDURE dbo.UpdateUser
(
#UserID int,
#Name nvarchar(40),
#Address nvarchar(60),
#City nvarchar(15),
)
AS
SET NOCOUNT OFF;
UPDATE [dbo].[User] SET [UserID] = #UserID, [Name] = #Name, [Address] = #Address, [City] = #City,WHERE (([UserID] = #UserID));
PROCEDURE:
CREATE PROCEDURE dbo.UpdateUser
(
#UserID int,
#Name nvarchar(40),
#Address nvarchar(60),
#City nvarchar(15)
)
AS
BEGIN
INSERT INTO
dbo.LogUser (UserID, Name, Address, City)
SELECT
UserID, Name, Address, City
FROM
dbo.User
WHERE
UserID = #UserID;
UPDATE
dbo.User
SET
UserID = #UserID,
Name = #Name,
Address = #Address,
City = #City
WHERE
UserID = #UserID;
END
How about inserting the data into the LogUser table before you update it?
-- Step 1: Add user history into LogUser table
INSERT INTO
dbo.LogUser (UserID, Name, Address, City)
SELECT
UserID, Name, Address, City
FROM
dbo.User
WHERE
UserID = #UserID;
-- Step 2: Update the user in the User table
UPDATE
dbo.User
SET
Name = #Name,
Address = #Address,
City = #City
WHERE
UserID = #UserID;
Create dbo.LogUser
create table dbo.LogUser (
action varchar(3),
changedAt datetime,
userID nchar(5), -- why not int ???
Name nvarchar(40),
Address nvarchar(60),
City nvarchar(15) )
Use trigger on dbo.User
create trigger dbo.User_UPD on dbo.User
for update, delete
as begin
insert dbo.LogUser
select case when i.UserID is null then 'Del' else 'Upd' end,
GETDATE(),
d.UserID,
d.Name,
d.Address,
d.City
from deleted d
left join inserted i on i.UserID = d.UserID
end
Update dbo.Users as You want.
Update dbo.Users
Set Name = 'Joe'

How to map table fields from source as the variables of stored procedure in destination in SSIS Package?

I want to transfer data from source Database table named Patient (which contains many rows) to destination database tables(2) named Person & Patient.
I already have stored procedure named AddPatient in destination database which will add person related fields to Person table and other fields to Patient table, so I would like to execute that procedure and to assign the fields from source database as variables to it. The following are the code of AddPatient sp in destination database.
ALTER PROCEDURE [dbo].[AddPatient]
(
#TenantId BIGINT,
#FirstName NVARCHAR(100),
#LastName NVARCHAR(100),
#PersonNumber NVARCHAR(20),
#MobileNumber NVARCHAR(20),
#EmailId NVARCHAR(100),
#Address NVARCHAR(255),
#City NVARCHAR(50),
#ZipCode NVARCHAR(20),
#ListComments NVARCHAR(1000),
#Comment NVARCHAR(500),
#AlternateEmailId NVARCHAR(100) ,
#HomePhone NVARCHAR(20) ,
#Relative NVARCHAR(255) ,
#HasDiabetes [bit],
#HasBlooPressure [bit],
#AddedBy BIGINT,
#AddedDateTime smalldatetime,
#PersonId BIGINT OUTPUT
)
AS
BEGIN
SET NOCOUNT ON
IF #TenantId IS NULL
RAISERROR('The value for #TenantID should not be null', 15, 1) -- with log
ELSE
BEGIN
DECLARE #new_person_id BIGINT
DECLARE #new_patient_id BIGINT
DECLARE #PatientIdentifier NVARCHAR(50)
EXEC dbo.GetNextPatientIdForTenant #TenantID, #PatientIdentifier OUTPUT
INSERT INTO dbo.Person
(
TenantId,
FirstName,
LastName,
PersonNumber,
MobileNumber,
EmailId,
Address,
City,
ZipCode,
AddedBy,
AddedDateTime
)
VALUES
(
#TenantId,
#FirstName,
#LastName,
#PersonNumber,
#MobileNumber,
#EmailId,
#Address,
#City,
#ZipCode,
#AddedBy,
#AddedDateTime
)
SELECT #new_person_id = SCOPE_IDENTITY()
INSERT INTO dbo.Patient
(
TenantId,
PatientIdentifier,
PersonId,
ListComments,
Comment,
AlternateEmailId,
HomePhone,
Relative,
HasDiabetes,
HasBlooPressure,
AddedBy,
AddedDateTime
)
VALUES
(
#TenantId,
#PatientIdentifier,
#new_person_id,
#ListComments ,
#Comment ,
#AlternateEmailId,
#HomePhone ,
#Relative ,
#HasDiabetes,
#HasBlooPressure,
#AddedBy ,
#AddedDateTime
)
SELECT #new_patient_id = SCOPE_IDENTITY()
SELECT #PersonId = #new_person_id
SELECT #new_patient_id
END
END
There is no TenantId & AddedBy field in source, so I want to assign both as 1 for all rows to be transfered.
I know Execute SQL Task will handles stored procedure and for each row data Foreach Loop Container will take care in SSIS. But I don't know how to assign the variables of sp in destination database to the fields of table from source database.
Anyone help me with this.
Thanks in advance !

Pass Output Value of one stored procedure to another stored procedure

I want to use output value of one stored procedure in another stored procedure .
Stored procedure 1:
Create PROCEDURE [dbo].[usp_AddUpdateUser]
#UserId INT,
#Email varchar(50),
#FirstName varchar(50)
AS
BEGIN
MERGE [User] AS target
USING (SELECT #UserId) AS source (Id)
ON target.Id = source.Id
WHEN MATCHED THEN
UPDATE
SET Email = #Email,
FirstName = #FirstName
WHEN NOT MATCHED THEN
INSERT (Email, FirstName)
VALUES (#Email, #FirstName)
OUTPUT inserted.Id;
END
Now I want to use the inserted Id of above stored procedure to below stored procedure:
ALTER PROCEDURE usp_AddUpdateDealer
(#Id INT,
#DealerName varchar(55),
#Email varchar(55),
#UserId INT)
AS
BEGIN
DECLARE #NewUserId INT
EXEC #NewUserId = usp_AddUpdateUser #UserId, #Email, #DealerName
MERGE Dealer AS target
USING (SELECT #Id) AS source (Id) ON target.Id = source.Id
WHEN MATCHED THEN
UPDATE
SET #DealerName = #DealerName,
Email = #Email,
UserId = #NewUserId
WHEN NOT MATCHED THEN
INSERT (DealerName, Email, UserId)
VALUES (#DealerName, #Email, #NewUserId)
OUTPUT inserted.Id;
END
#NewUserId not gives the output value.
How can I got the output option of the usp_AddUpdateUser stored procedure to use that in next statement?
ALTER PROCEDURE usp_AddUpdateDealer
(
#Id INT,
#DealerName varchar(55),
#Email varchar(55),
#UserId INT
)
AS
BEGIN
DECLARE #t table(NewUserId INT )
INSERT #t(NewUserId)
EXEC #NewUserId = usp_AddUpdateUser #UserId,#Email,#DealerName
DECLARE #NewUserId INT
SELECT #NewUserId = NewUserId FROM #t
MERGE Dealer AS target
USING (SELECT #Id) AS source (Id)
ON target.Id = source.Id
WHEN MATCHED THEN
UPDATE
SET #DealerName = #DealerName,
Email = #Email,
UserId=#NewUserId
WHEN NOT MATCHED THEN
INSERT (DealerName,Email,UserId)
VALUES (#DealerName,#Email,#NewUserId)
OUTPUT inserted.Id;
END