Capture actual SQL statement running as part of Dynamic Sql in SQLServer - sql-server

I am using SQL Server 2017. It has SP which inserts data into table passed via parameters.
create table employee(
id int,
first_name varchar(100),
last_name varchar(100),
salary int,
city varchar(100)
)
Create PROCEDURE MasterInsertUpdateDelete
(
#id INTEGER,
#first_name VARCHAR(10),
#last_name VARCHAR(10),
#salary INTEGER,
#city VARCHAR(20),
#StatementType nvarchar(20) = ''
)
AS
BEGIN
IF #StatementType = 'Insert'
BEGIN
insert into employee (id,first_name,last_name,salary,city) values( #id, #first_name, #last_name, #salary, #city)
END
IF #StatementType = 'Select'
BEGIN
select * from employee
END
END
Suppose I execute SP using below statement
exec MasterInsertUpdateDelete
#id = 1,
#first_name = 'Vijay',
#last_name = 'Kumar',
#salary = 100,
#city = 'Pune',
#StatementType = 'Insert'
Now I want to capture original query (replace with parameter values) which is being executed on DB server
i.e. insert into employee (id,first_name,last_name,salary,city) values( 1, 'Vijay', 'Kumar', 100, 'Test')
I used SQL profiler and exended events but it gives me SQL statement along with parameters
i.e.
insert into employee (id,first_name,last_name,salary,city) values( #id, #first_name, #last_name, #salary, #city)
Practically I have more complex store procedure as Dynamic SQL. I would like to capture the executing statement with replace variable values. How can we achieve it in MS SQL Server?

Related

Stored Procedure For Updating Records And Deleting Records Using Dynamic SQL

I have a stored procedure that inserts records into the specified schema table using dynamic sql. To specify which schema you would like to insert into, it is done as follows:
EXEC Stored_Procedure_Name '[Schema_Name]', 'value1', 'value2', ...
The problem that I now face is that I wish to have a similar stored procedure that updates the table within a specified schema, as well as delete records within a specified schema's table, but I can't wrap my head around a method to accomplish this.
I've tried changing the code to make it UPDATE (instead of INSERT INTO) the tables but the logic behind it doesn't make any sense.
if object_id('AddUser_proc', 'AU') is not null
begin
drop proc AddUser_proc;
end;
GO
CREATE PROCEDURE AddUser_proc
(#branch VARCHAR(50),
#UserID NUMERIC(13,0),
#Username VARCHAR(50),
#Email VARCHAR(50),
#Fullname VARCHAR(100),
#Password BINARY(64),
#DateCreated DATETIME,
#ClosestBranch VARCHAR(50) )
AS
BEGIN
IF EXISTS(
SELECT 1
FROM Sys.Schemas
WHERE name = #branch
)
BEGIN
DECLARE #dynamic nvarchar(4000),
#paramDefinition nvarchar(4000)
SELECT #dynamic = N'INSERT INTO '+ quotename(#branch) + N'.User_tbl (
UserID,
Username,
Email,
Fullname,
Password,
DateCreated,
ClosestBranch
)
VALUES(#UserID, #Username, #Email, #Fullname, #Password, #DateCreated, #ClosestBranch)',
#paramDefinition =
N'#UserID numeric(13,0),
#Username varchar(50),
#Email varchar(50),
#Fullname varchar(100),
#Password binary(64),
#DateCreated datetime,
#ClosestBranch varchar(50)'
EXEC sp_executeSql #dynamic, #paramDefinition, #UserID, #Username, #Email, #Fullname, #Password,
#DateCreated, #ClosestBranch;
END
END
GO
Above is the code I have for the INSERT stored procedure. I would appreciate it if you could help me with a similar stored procedure that updates tables, as well as one that deletes records from tables. It would be ideal if all three (INSERT, UPDATE and DELETE) procedures could be contained within one stored procedure with the use of IF statements.
EDITED:
This is the SP that I have:
if object_id('UpdateUser_proc', 'UU') is not null
begin
drop proc UpdateUser_proc;
end;
GO
CREATE PROCEDURE UpdateUser_proc(
#branch varchar(50),
#UserID numeric(13,0),
#Username varchar(50),
#Email varchar(50),
#Fullname varchar(100),
#Password binary(64),
#DateCreated datetime,
#ClosestBranch varchar(50)
)
AS
BEGIN
IF EXISTS(
SELECT 1
FROM Sys.Schemas
WHERE name = #branch
)
BEGIN
DECLARE #dynamic nvarchar(4000),
#paramDefinition nvarchar(4000)
SELECT #dynamic = N'UPDATE '+ quotename(#branch) + N'.User_tbl
SET Username=#Username,Email=#Email,Fullname=#Fullname,Password=#Password,DateCreated=#DateCreated,ClosestBranch=#ClosestBranch
WHERE UserID=#UserID',
#paramDefinition =
N'#UserID numeric(13,0),
#Username varchar(50),
#Email varchar(50),
#Fullname varchar(100),
#Password binary(64),
#DateCreated datetime,
#ClosestBranch varchar(50)'
EXEC sp_executeSql #dynamic, #paramDefinition, #UserID, #Username, #Email, #Fullname, #Password,
#DateCreated, #ClosestBranch;
END
END
GO
And I execute it like so:
EXEC UpdateUser_proc 'Bloemfontein', '7928623003512', 'klover', 'test.g#g.com', 'John Snow', 'password', '20190303', 'Bloemfontein'
I have discovered that the problem may be due to the fact that the password field is a binary(64) type and I can't seem to use the HASHBYTES function within execution of the SP
As far as the DELETE SP goes, I have not attempted it yet as I have been stuck with the UPDATE SP.

understanding workings of cursor

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;
...

SQL Server procedure error - duplicate entries inserted into the table

I am writing a procedure in SQL Server to insert or update records.
The update part of the code is working fine but when I am executing it for inserting, duplicate entries are inserted into the table.
I created the primary key to avoid this error but after creating that I am not able to insert any single record.
Here is the code :
Alter Procedure test_case
#id int,
#name nvarchar(20)
AS
If exists (Select t_id from testing2 where t_id = #id)
begin
update testing2
set t_id = #id, t_name = #name
where t_id = #id
end
else
begin
insert into testing2 (t_id, t_name, last_date, hard)
select
#id, #name, convert(date, getdate()), 'null'
from test
end
On executing it is showing 2 rows affected
You do not require test table in the select query
insert into testing2 (t_id, t_name, last_date, hard)
select
#id as t_id, #name as t_name, convert(date, getdate()) as last_date, 'null' as hard
is enough
I like to break functionality into smaller parts because it helps me to manage code better.
Maybe this is not a good example since it is pretty simple but I will write it anyway.
Create Procedure Testing2_InsertData (
#id int,
#name nvarchar(20)
) As
Set NoCount On
Insert Into testing2
(t_id, t_name, last_date, hard)
Values
( #id, #name, GetDate(), null )
Go
Create Procedure Testing2_UpdateData (
#id int,
#name nvarchar(20)
) As
Set NoCount On
Update testing2 Set
t_name = #name --, maybe last_date = GetDate()
Where ( t_id = #id )
Go
Create Procedure Testing2_SaveData (
#id int,
#name nvarchar(20)
) As
Set NoCount On
If ( Exists( Select t_id From testing2 Where ( t_id = #id ) ) )
Exec Testing2_UpdateData #id, #name
Else
Exec Testing2_InsertData #id, #name
Go

SQL Server: Use output parameter values in the same procedure also

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()

SQL Server error.Explicit value for Identity Column....can only be specified when a column list is used and IDENTITY_INSERT is ON

I am trying to create procedure, which is generating an error stating
An explicit value for the identity column in table tblRegisterUser can only be specified when a column list is used and IDENTITY_INSERT is ON.
I tried to surround insert statement with INDENTITY_INSERT to ON,but that too doesn't work. am I missing anything or is it an error with the sub query which i included?
Following is the stored procedure
CREATE PROCEDURE dbo.spInsertUserRegister
(
#FirstName nvarchar(50),
#LastName nvarchar(50),
#Username nvarchar(50),
#Password nvarchar(50),
#Designation nvarchar(50),
#Department nvarchar(50),
#IsAdmin bit
)
AS
BEGIN
INSERT INTO tblRegisterUser Values
(
#FirstName, #LastName, #Username, #Password,#Designation,#Department,#IsAdmin
)
DECLARE #UID INT
SET #UID = ##IDENTITY
INSERT INTO tblLogin(Username,Password,UID,IsAdmin)
Values(#Username, #Password, #UID,(SELECT IsAdmin FROM tblRegisterUser WHERE Username=#Username AND Password=#Password))
END
If the structure of the tblRegisterUser table is something like
ID int primary_key autoincrement
FirstName varchar
LastName varchar
Username varchar
Password varchar
Designation varchar
Department varchar
IsAdmin bit
than this statement is wrong:
INSERT INTO tblRegisterUser Values
(
#FirstName, #LastName, #Username, #Password,
#Designation,#Department,#IsAdmin
)
You should use an explicit column list to specify the columns:
INSERT INTO tblRegisterUser
( FirstName, LastName, Username, Password,
Designation, Department, IsAdmin)
VALUES
(
#FirstName, #LastName, #Username, #Password,
#Designation,#Department,#IsAdmin
)
This way the ID field is automatically populated, and the ##Identity statement should return it correctly.
That said, SQL Server has a few functions that return the generated ID for the last rows, each with it's own specific strengths and weaknesses.
Basically:
##IDENTITY works if you do not use triggers
SCOPE_IDENTITY() works for the code you explicitly called.
IDENT_CURRENT(‘tablename’) works for a specific table, across all scopes.
In almost all scenarios SCOPE_IDENTITY() is what you need, and it's a good habit to use it, opposed to the other options.
A good discussion on the pros and cons of the approaches is also available here.
copied from this answer
Try this one -
CREATE PROCEDURE dbo.spInsertUserRegister
(
#FirstName NVARCHAR(50),
#LastName NVARCHAR(50),
#Username NVARCHAR(50),
#Password NVARCHAR(50),
#Designation NVARCHAR(50),
#Department NVARCHAR(50),
#IsAdmin BIT
)
AS BEGIN
INSERT INTO dbo.tblRegisterUser (FirstName, LastName, Username, [Password], Designation, Department, IsAdmin)
SELECT #FirstName, #LastName, #Username, #Password, #Designation, #Department, #IsAdmin
DECLARE #ID BIGINT
SELECT #ID = SCOPE_IDENTITY()
--SET IDENTITY_INSERT dbo.tblLogin ON;
INSERT INTO dbo.tblLogin (UserName, [password], [uid], IsAdmin)
SELECT #Username, #Password, #ID, IsAdmin
FROM tblRegisterUser
WHERE UserName = #Username
AND [password] = #Password
--SET IDENTITY_INSERT dbo.tblLogin OFF;
END

Resources