How To Filter SQL Results Efficiently - sql

I have a requirement to filter the table records based on passed input criteria to the SQL stored procedure. To make it simple, I'm elaborating the question using simple Employee table (I'm using MSSQL server for my project):
Table: Employee(Id, FirstName,LastName, Designation)
The stored procedure takes 3 input arguments (Id, LastName, Designation). This will be invoked with at-least one input parameter (other parameter will be set to empty string (in case of LastName, Designation) or 0 (In case of Id)).
Here is the stored procedure:
Create Procedure GetEmployees
(
#id int,
#lastName varchar(30),
#designation varchar(30)
)
AS
BEGIN
Create Table #employees (Id int, FirstName varchar(30),LastName varchar(30), Designation varchar(30);
IF(#id != 0)
BEGIN
Insert Into #employees (Id,FirstName,LastName,Designation)
Select Id, FirstName,LastName,Designation From Employee Where Id = #id
END
IF(#lastName != ‘’)
BEGIN
Insert Into #employees (Id,FirstName,LastName,Designation)
Select Id, FirstName,LastName,Designation From Employee Where LastName = #lastName
END
IF(designation != ‘’)
BEGIN
Insert Into #employees (Id,FirstName,LastName,Designation)
Select Id, FirstName,LastName,Designation From Employee Where Designation = #designation
END
-- Returning filtered record set to the application layer
Select Id,FirstName,LastName,Designation From #employees;
END
GO
I think there a lot of code repetition in the stored procedure. Is there an efficient way of solving this scenario ?

You may use a single insert query with a WHERE clause which covers all the logic:
BEGIN
INSERT INTO #employees (Id, FirstName, LastName, Designation)
SELECT Id, FirstName, LastName, Designation
FROM Employee
WHERE
(Id = #id AND #id <> 0) OR
(LastName = #lastName AND #lastName <> '') OR
(Designation = #designation AND #designation <> '');

You can use Case condition in Where clause, Find Below Query.
INSERT INTO #employees (Id, FirstName, LastName, Designation)
SELECT Id, FirstName, LastName, Designation
FROM Employee
WHERE
(Id = CASE WHEN #id = 0 THEN ID ELSE #ID END) OR
(LastName = CASE WHEN #lastName = '' THEN LastName ELSE #lastName END) OR
(Designation = CASE WHEN #designation = '' THEN Designation ELSe #designation END);
We can also Use AND Instead of OR in Where clause.

As #trincot said, there is no need for a temporary table. My suggestion is:
Create Procedure GetEmployees
(
#id int,
#lastName varchar(30),
#designation varchar(30)
)
AS
BEGIN
Select Id, FirstName,LastName,Designation From Employee
Where (#id>'' AND Id = #id)
OR (#lastName>'' AND LastName = #lastName)
OR (#designation>'' AND Designation = #designation);
END
GO

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.

Stored procedure with multiple insert

I want a value of #temp table and People table value together. I can use json_modify but it will work only for single value insert. But in case of multiple values, we can make both in JSON object which will be array of object and combine both. But how?
CREATE PROCEDURE [dbo].[SpPersonIns]
(#json nvarchar(max) OUTPUT)
AS
BEGIN
CREATE TABLE #temp (PersonId int)
INSERT INTO People (FirstName, MiddleName, LastName, SSN, UserPersonId)
OUTPUT inserted.PersonId
INTO #temp
SELECT p.firstName, p.middleName, p.lastName, p.ssN, p.userPersonId
FROM OPENJSON(#json) WITH(
firstName varchar(100),
middleName varchar(100),
lastName varchar(100),
ssN varchar(9),
userPersonId int
) as p
declare #p nvarchar(max);
select #p = t.PersonId from #temp as t
SELECT #json = JSON_MODIFY(#json,'$.PersonId',#p)
END
You have two options.
The simplest is to just output the whole inserted and then rebuild the JSON from it
CREATE OR ALTER PROCEDURE [dbo].[SpPersonIns]
#json nvarchar(max) OUTPUT
AS
DECLARE #temp TABLE (
PersonId int,
firstName varchar(100),
middleName varchar(100),
lastName varchar(100),
ssN varchar(9),
userPersonId int
);
INSERT INTO People (FirstName, MiddleName, LastName, SSN, UserPersonId)
OUTPUT inserted.PersonId, inserted.firstName, inserted.middleName, inserted.lastName, inserted.ssN, inserted.userPersonId
INTO #temp (PersonId, firstName, middleName, lastName, ssN, userPersonId)
SELECT p.firstName, p.middleName, p.lastName, p.ssN, p.userPersonId
FROM OPENJSON(#json)
WITH (
firstName varchar(100),
middleName varchar(100),
lastName varchar(100),
ssN varchar(9),
userPersonId int
) as p;
SET #json = (
SELECT *
FROM #temp
FOR JSON PATH
);
Or, if you have a unique natural key within the JSON (for example userPersonId), you can use some kind of join.
CREATE OR ALTER PROCEDURE [dbo].[SpPersonIns]
#json nvarchar(max) OUTPUT
AS
DECLARE #temp TABLE (PersonId int not null, userPersonId int PRIMARY KEY);
INSERT INTO People (FirstName, MiddleName, LastName, SSN, UserPersonId)
OUTPUT inserted.PersonId, inserted.userPersonId
INTO #temp (PersonId, userPersonId)
SELECT p.firstName, p.middleName, p.lastName, p.ssN, p.userPersonId
FROM OPENJSON(#json)
WITH (
firstName varchar(100),
middleName varchar(100),
lastName varchar(100),
ssN varchar(9),
userPersonId int
) as p;
SET #json = (
SELECT
'[' +
STRING_AGG(
JSON_MODIFY(
arr.value,
'$.PersonId',
t.PersonId
),
','
) +
']'
FROM OPENJSON(#json) AS arr -- if you don't specify a schema then you get the whole array object
JOIN #temp t ON t.userPersonId = JSON_VALUE(arr.value, '$.userPersonId')
);

How to write dynamic insert statement in sql server?

I have a Student table and Department table.
Student Table contains Three columns
StudentId | DeptId | StudentName
Department Table contains
DeptId | DeptName
It may be the case that DeptTable sometimes doesnot exists. That is I am deploying script with two scenarios sometimes with department and sometimes without. In the case without department , departmentId will not be there in student table
So when Department table exists I have to insert value of DeptId otherwise I have to write insert statement without departmentId
IF (EXISTS(
SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Department'
AND column_name = 'DeptId'
))
DECLARE #DeptId UNIQUEIDENTIFIER;
SET #DeptId = Select DeptId From Department Where DeptName = 'Computer'
INSERT INTO Student ([DeptId], [StudentName])
VALUES (#DeptId, 'TBAG')
But as DeptId column sometimes not present I need to create dynamic Sql.
So I created Select statement
DECLARE #sqlCommand nvarchar(1000)
DECLARE #DeptName varchar(75)
declare #DeptId uniqueIdentifier
SET #DeptName = 'Computer'
SET #sqlCommand = 'SELECT #dept=DeptId FROM customers WHERE DeptName = #DeptName'
EXECUTE sp_executesql #sqlCommand, N'#DeptName varchar(50),#dept uniqueIdentifier OUTPUT', #DeptName = #DeptName, #dept=#DeptId OUTPUT
But how to write Insert statement I am not getting
CREATE TABLE dbo.Student
(
DeptId UNIQUEIDENTIFIER,
StudentName VARCHAR(20)
)
GO
DECLARE #DeptId UNIQUEIDENTIFIER
IF COL_LENGTH('dbo.Department', 'DeptId') IS NOT NULL BEGIN
SELECT #DeptId = DeptId
FROM dbo.Department
WHERE DeptName = 'Computer'
END
INSERT INTO dbo.Student(DeptId, StudentName)
SELECT #DeptId /* null is when DeptId is not exists */, 'TBAG'
Output -
(1 row(s) affected)
As per my understanding you want to prepare dynamic insert statement for student table with two cases (with deptid or without deptid)
Please refer the code below.
DECLARE #STUDENTID INT='',--MENTION YOUR STUDENT ID VALUE AND USE THE SAME IN THE ELSE PART COMMENT OUT IF IT HAS IDENTITY
#STUDENTNAME VARCHAR(100)=''--MENTION YOUR STUDENT NAME
IF Col_length('dbo.Student', 'DeptId') IS NOT NULL
BEGIN
INSERT INTO dbo.Student
(DeptId,
StudentName)
SELECT DeptId,
'TBAG'
FROM dbo.Department
WHERE DeptName = 'Computer'
END
ELSE
BEGIN
INSERT INTO dbo.Student
(StudentName)
SELECT #STUDENTNAME
END

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 !

Upsert SQL query

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.