I am trying to insert a record into a table using MERGE.
If EmployeeCode doesn't exist in the table it should insert the a new record.
My Code is like this
DECLARE #EmployeeCode BIGINT = 1234,
#FirstName VARCHAR(250) = 'FirstName',
#MiddleName VARCHAR(250) = 'LastName',
#LastName VARCHAR(250) = 'LastName',
#Nationality VARCHAR(250) = 'Nationality',
#BloodGroup VARCHAR(4) = 'A+',
#Gender VARCHAR(7) = 'Male'
MERGE dbo.tbTableName AS tb1
USING ( SELECT tbTableName.EmployeeCode
FROM dbo.tbTableName tbTableName
WHERE tbTableName.EmployeeCode = #EmployeeCode) AS tb2 (EmployeeCode)
ON (tb1.EmployeeCode = tb2.EmployeeCode)
WHEN NOT MATCHED THEN
INSERT ( EmployeeCode, FirstName, MiddleName, LastName,Nationality, BloodGroup, Gender )
VALUES ( #EmployeeCode, #FirstName, #MiddleName, #LastName, #Nationality, #BloodGroup, #Gender );
The issue I am having is that even when the EmployeeCode doesn't match any records in the table its not inserting.
I have used an alternative way to solve the problem using by using IF statement like this:
DECLARE #EmployeeCode BIGINT = 1234,
#FirstName VARCHAR(250) = 'FirstName',
#MiddleName VARCHAR(250) = 'LastName',
#LastName VARCHAR(250) = 'LastName',
#Nationality VARCHAR(250) = 'Nationality',
#BloodGroup VARCHAR(4) = 'A+',
#Gender VARCHAR(7) = 'Male'
IF NOT EXISTS ( SELECT tbTableName.EmployeeCode
FROM dbo.tbTableName tbTableName
WHERE tbTableName.EmployeeCode = #EmployeeCode)
BEGIN
INSERT INTO dbo.tbTableName (EmployeeCode, FirstName, MiddleName, LastName,Nationality, BloodGroup, Gender )
VALUES (#EmployeeCode, #FirstName, #MiddleName, #LastName, #Nationality, #BloodGroup, #Gender )
END
Now is the time to go read the documentation and study the first example. That example shows the conversion from a tradition if/else update/insert code block to a single merge statement. Notice how the table source is defined in the example. You are selecting a row from a table (which you know does not exist) - so there are effectively no rows to be used for either insert or update. That is why nothing is inserted. The source of your merge information should be your set of variables just like the documentation example. An abbreviated version:
set nocount on;
DECLARE #EmployeeCode BIGINT = 1234,
#FirstName VARCHAR(250) = 'FirstName',
#MiddleName VARCHAR(250) = 'LastName',
#LastName VARCHAR(250) = 'LastName',
#Nationality VARCHAR(250) = 'Nationality',
#BloodGroup VARCHAR(4) = 'A+',
#Gender VARCHAR(7) = 'Male';
declare #tbl table (EmployeeCode bigint, FirstName varchar(250));
--insert #tbl(EmployeeCode, FirstName) values (1234, 'zork');
MERGE #tbl AS tb1
USING ( SELECT #EmployeeCode, #FirstName) as src(EmployeeCode, FirstName)
ON (tb1.EmployeeCode = src.EmployeeCode)
WHEN NOT MATCHED THEN
INSERT ( EmployeeCode, FirstName )
VALUES ( #EmployeeCode, #FirstName );
select ##ROWCOUNT, * from #tbl;
Related
I've created the following stored procedure:
CREATE PROCEDURE dbo.InsertTutees
#StudentID INT = NULL,
#FirstName VARCHAR(50) = NULL,
#Surname VARCHAR(50) = NULL,
#Major VARCHAR(50) = NULL,
#Timestamp DATETIME = NULL
AS
BEGIN
SET NOCOUNT ON
INSERT INTO dbo.Tutees (StudentID, FirstName, Surname, Major, Timestamp)
VALUES (#StudentID, #FirstName, #Surname, #Major, #Timestamp)
END
GO
EXEC dbo.InsertTutees
#StudentID = 2,
#FirstName = 'Sarah',
#Surname = 'Smith',
#Major = 'Science',
#Timestamp = '2013-12-12';
However I would like to replace the timestamp value with the current timestamp. I've tried GETDATE(), but I get the following error:
Msg 102, Level 15, State 1, Line 6
Incorrect syntax near ')'
Is there an alternative way to retrieve the current date inside this exec statement? Should I edit my stored procedure?
Thanks for your help!
EDIT:
This is the exec statement I try to complete:
EXEC dbo.InsertTutees
#StudentID = 2,
#FirstName = 'Sarah' ,
#Surname = 'Smith' ,
#Major = 'Science' ,
#Timestamp = getdate()
You can use an intermediate variable:
DECLARE #dt datetime =GETDATE()
EXEC dbo.InsertTutees
#StudentID = 2,
#FirstName = 'Sarah' ,
#Surname = 'Smith' ,
#Major = 'Science' ,
#Timestamp = #dt
If you look it up in the documentation, a default has to be a constant or NULL. In order to have a default value that equates to GETDATE(), you'd need some code within the proc along these lines:
IF #Timestamp IS NULL
BEGIN
SET #Timestamp = GETDATE();
END
That would allow you to both provide a value and supply a default as needed.
I created a stored procedure but when I tried to use it I got error.
"Msg 8114, Level 16, State 5, Procedure SP_Customer_Add, Line 0 [Batch
Start Line 36] Error converting data type varchar to
uniqueidentifier."
alter proc SP_Customer_Add (
#UserID uniqueidentifier,
#Firstname nvarchar(30),
#Surname nvarchar(30),
#CardNumber nvarchar(16),
#Password nvarchar(4),
#City nvarchar(13),
#Birthdate smalldatetime,
#Email nvarchar(30),
#PhoneNumber nvarchar (10),
#Balance money
)
as
begin
set nocount on;
insert into Customers (
[UserID],
[Firstname],
[Surname],
[CardNumber],
[Password],
[City],
[Birthdate],
[Email],
[PhoneNumber],
[Balance]
) values (
#UserID,
#Firstname,
#Surname,
#CardNumber,
#Password,
#City,
#Birthdate,
#Email ,
#PhoneNumber ,
#Balance
)
select #UserID = newID();
END
I call the procedure like:
exec SP_Customer_Add
'Cem',
'Yücel',
'1234567891234567',
'1111','Malatya',
'1999/12/5',
'Cemyucel#yahoo.com',
'5421234312',
10000
If you're inserting rows with a GUID, then let the RDBMS handle the generation. I assume that the ID is also the CLUSTERED INDEX so change it so that it has a default value:
ALTER TABLE dbo.Customers ADD CONSTRAINT DF_UserID DEFAULT NEWSEQUENTIALID() FOR UserID;
Then DROP your SP and recreate it with the name Customer_Add (because having the prefix is a problem, as my comment says) and recreate the SP without any references to UserID as a variable or to INSERT.
Your are not passing a value for the parameter (#UserID) when calling the procedure SP_Customer_Add
exec SP_Customer_Add
'Cem',
'Yücel',
'1234567891234567',
'1111','Malatya',
'1999/12/5',
'Cemyucel#yahoo.com',
'5421234312',
10000
Below is the altered procedure:
alter proc SP_Customer_Add
(
#Firstname nvarchar(30),
#Surname nvarchar(30),
#CardNumber nvarchar(16),
#Password nvarchar(4),
#City nvarchar(13),
#Birthdate smalldatetime,
#Email nvarchar(30),
#PhoneNumber nvarchar (10),
#Balance money
)
as
begin
set nocount on;
DECLARE #UserID uniqueidentifier
SET #UserID = NEWID()
insert into Customers ([UserID],[Firstname], [Surname], [CardNumber], [Password], [City], [Birthdate], [Email], [PhoneNumber], [Balance] )
values
(
#UserID,
#Firstname,
#Surname,
#CardNumber,
#Password,
#City,
#Birthdate,
#Email ,
#PhoneNumber ,
#Balance
)
END
Let's say I have have the following parameters:
#firstName varchar(50), #middleName varchar(50), #LastName varchar(50)
I want the middle name to be optional, How do I return the value (#portalUserName) without middle name if the middle name is not passed as an input parameter?
PS: I need the middle names first character to be added in the user name if the user has a middle name (i.e. if middle name value isn't null)
CREATE PROCEDURE [dbo].[cst_sproc_UserName_Get]
(#firstName VARCHAR(50),
#middleName VARCHAR(50),
#LastName VARCHAR(50)
)
AS
BEGIN
DECLARE #portalUserName VARCHAR(50)
SET #portalUserName = SUBSTRING(UPPER(RTRIM(#firstname)), 1, 1) +
SUBSTRING(UPPER(RTRIM(#firstname)), 1, 1) +
LOWER(RTRIM(#LastName))
IF NOT EXISTS (SELECT 'TRUE' FROM wpUser WHERE UserCode = #portalUserName)
BEGIN
SELECT #portalUserName UserCode
RETURN
END
END
To make a parameter optional, you need to give it a default value, which could be NULL.
CREATE PROC [dbo].[cst_sproc_UserName_Get]
(
#firstName varchar(50),
#middleName varchar(50) = NULL,
#LastName varchar(50)
)
AS
BEGIN
Declare #portalUserName varchar(50)
SET #portalUserName = SUBSTRING(upper(RTRIM(#firstname)),1,1)+ SUBSTRING(upper(RTRIM(#firstname)),1,1) + lower(RTRIM(#LastName))
IF NOT EXISTS (SELECT 'TRUE' FROM wpUser WHERE UserCode = #portalUserName)
BEGIN
SELECT #portalUserName UserCode
Return
END
But to call it, you need to follow Best Practices and call your procedure with parameter naming.
EXEC [dbo].[cst_sproc_UserName_Get] #firstName = 'Luis', #LastName = 'Cazares';
Trying to change some stores procedures and query's for better performance. For some part, it's rewriting cursor syntax. To do this,I must fully understand how they work. I tried this simple ETL example, but it does not give me the expected result. Basically, doing an UPSERT here with a cursor.
Example code:
CREATE TABLE #Destination
(PersonID INT, FirstName VARCHAR(10), LastName VARCHAR (10))
INSERT INTO #Destination VALUES (101, 'M', 'Donalds')
INSERT INTO #Destination VALUES (102, NULL, 'Richards')
INSERT INTO #Destination VALUES (103, 'Rianna', 'Lock')
INSERT INTO #Destination VALUES (104, 'Leo', 'Svensson')
CREATE TABLE #SourceTable
(PersonID INT, FirstName VARCHAR(10), LastName VARCHAR (10))
INSERT INTO #Destination VALUES (102, 'Diana', 'Richards')
INSERT INTO #SourceTable VALUES (103, 'Rianna', 'Locks')
INSERT INTO #SourceTable VALUES (106, 'Cleo', 'Davung')
DECLARE #PersonID INT
DECLARE #Firstname VARCHAR (10)
DECLARE #Lastname VARCHAR (10)
DECLARE SimpleCursor CURSOR FOR
SELECT PersonID, FirstName, LastName
FROM #SourceTable
Open SimpleCursor
FETCH NEXT FROM SimpleCursor INTO #PersonID, #Firstname, #Lastname
WHILE ##FETCH_STATUS = 0
BEGIN
IF EXISTS ( SELECT PersonID FROM #Destination
WHERE PersonID = #PersonID )
UPDATE #Destination
SET #Destination.FirstName = #SourceTable.FirstName,
#Destination.LastName = #SourceTable.LastName
FROM #SourceTable
WHERE #Destination.PersonID = #SourceTable.PersonID
ELSE
INSERT INTO #Destination
SELECT PersonID, Firstname, Lastname FROM #SourceTable
FETCH NEXT FROM SimpleCursor INTO #PersonID, #Firstname, #Lastname
END
CLOSE SimpleCursor
DEALLOCATE SimpleCursor
SELECT * FROM #Destination
What am I missing here? I am not updating anything, while PersonID 102 and 103 do exist.
Thanks a lot.
You're not using the variables you fetched the values into in your UPDATE or INSERT statements. Try:
...
IF EXISTS (SELECT *
FROM #Destination
WHERE PersonID = #PersonID)
BEGIN
UPDATE #Destination
SET FirstName = #FirstName,
LastName = #LastName
WHERE PersonID = #PersonID;
END
ELSE
BEGIN
INSERT INTO #Destination
(PersonID,
FirstName,
LastName)
VALUES (#PersonID,
#FirstName,
#LastName);
END;
...
I have this stored procedure:
CREATE PROCEDURE [dbo].[AddEmployee]
(
#employeeId int OUTPUT,
#firstName varchar(50),
#lastName varchar(50),
#password varchar(100)
)
AS
BEGIN
INSERT INTO Employees(FirstName, LastName)
VALUES(#firstName, #lastName)
INSERT INTO Logins(Password, EmployeeId)
VALUES(#password, #employeeId)
SELECT ##Identity
END
GO
Let,
#employeeId = EmployeeId
generated during insert in Employees
After that, I was trying to insert the #employeeId into the Logins table.
Also, the #employeeId is used as OUTPUT parameter.
How should I do it?
May be we can use INSERTED.EmployeeId, but I don't know how to use it.
CREATE PROCEDURE [dbo].[AddEmployee] (
#firstName VARCHAR(50)
, #lastName VARCHAR(50)
, #password VARCHAR(100)
, #employeeId INT OUTPUT
)
AS
BEGIN
INSERT INTO Employees (
FirstName
, LastName
)
VALUES (
#firstName
, #lastName
)
SELECT #employeeId = SCOPE_IDENTITY()
INSERT INTO Logins (
Password
, EmployeeId
)
VALUES (
#password
, #employeeId
)
END
GO
EXEC [dbo].[AddEmployee] #firstname = 'test'
, #lastname = 'tester'
, #password = '321321'
, #employeeId = ''
Assuming your EmployeeId column is an IDENTITY column, you should use the OUTPUT clause to fetch the value (you output it into a table variable):
DECLARE #empId TABLE (empId int)
INSERT INTO Employees
(
FirstName, LastName
)
OUTPUT INSERTED.EmployeeId INTO #empId
VALUES
(
#firstName,
#lastName
)
Then to get the value from the table variable into a scalar variable, do:
SET #employeeId = (SELECT TOP 1 empId FROM #empId)
The ##Identity value is the last identity inserted in the current session regardless of scope. Hence, it could be the identity of a row inserted by a trigger. It's always better to use scope_identity() instead. For more details, see MSDN.
You can assign an output parameter like:
set #employeeId = scope_identity()