SQL Server Dynamic where clause query - sql

I am trying to create a dynamic query I just to do with linq
from a in Customers
where (string.IsNullOrEmpty(name)? true : a.FirstName == name) && (string.IsNullOrEmpty(last)? true : a.LastName == last)
select a;
but now I need to do in on Stored Procedure and I don't want to concatenate for security reason and performance. The most close example I found is this query
declare #Name as varchar(100)
declare #GroupName as varchar(100)
set #Name = ''
set #GroupName = 'Manufacturing'
SELECT TOP (1000) [DepartmentID]
,[Name]
,[GroupName]
,[ModifiedDate]
FROM [AdventureWorks2017].[HumanResources].[Department]
where ([Name] = case
when #Name is null or #Name = '' then null
else #Name
end
)
and
(
[GroupName] = case
when #GroupName is null or #GroupName = '' then null
else #GroupName
end
)
This almost works. I think this should be the answer but the where clause fails for obvious reason.
I would like the where clause could produce '1=1' if the param "Name" or "GroupName" is null
example
#Name = "Somename"
#GroupName = null
where (Name = #Name) and (1 = 1)
--or
#Name = null
#GroupName = "Somegruopname"
where (1 = 1) and (GroupName = #GroupName)
--
#Name = null
#GroupName = null
where (1 = 1) and (1 = 1)

You want it to succeed if the variable is null or empty or a match, so I would just write that in your stored procedure.
WHERE (#FirstName is null OR #FirstName = '' OR [FirstName] = #FirstName)
AND (#LastName is null OR #LastName = '' OR [LastName] = #LastName)

Please try this:
declare #Name as varchar(100)
declare #GroupName as varchar(100) = 'Manufacturing'
set #Name = LTRIM(RTRIM(#Name))
set #GroupName = 'Manufacturing'
SELECT TOP (1000) [DepartmentID]
,[Name]
,[GroupName]
,[ModifiedDate]
FROM [AdventureWorks2017].[HumanResources].[Department]
where ([Name] = coalesce(#Name,'') = '' OR [Name] = #Name)
and
([GroupName] = coalesce(#GroupName, '') = '' OR [GroupName] = #GroupName)

Related

Stored procedure with OUTPUT - Must declare the scalar variable

I'm writing a stored procedure that will be executed from C# to get data from database. Therefore I have to pass a GUID to this stored procedure and it should find data in table Contact or in the Lead table & return data back to C# app via output parameters.
When I try to execute this stored procedure in SSMS, I get a SQL exception
Must declare the scalar variable "#LastName"
Code:
ALTER PROCEDURE [api].[GetUser_NetId]
#NetId uniqueidentifier
, #LastName nvarchar(200) = '' OUTPUT
, #FirstName nvarchar(200) = '' OUTPUT
, #Country uniqueidentifier = NULL OUTPUT
, #Newsletter bit = 0 OUTPUT
AS
DECLARE
#Table SMALLINT
SET #Table = (
SELECT MIN(T.ID) FROM (
SELECT 100 AS [ID] FROM dbo.Contact WHERE Net_ID = #NetId
UNION ALL
SELECT 200 AS [ID] FROM dbo.Lead WHERE Net_ID = #NetId
) T
)
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = CONCAT(
' SELECT
#LastName = tbl.LastName,
#FirstName = tbl.FirstName,
#Country = tbl.Address1CountryId,
#Newsletter = tbl.Newsletter,
FROM
dbo.'
, CASE #Table
WHEN 100 THEN 'Contact'
WHEN 200 THEN 'Lead'
END
, ' as tbl
WHERE 1=1
AND tbl.Net_Id = '''
, #NetId
, ''''
)
EXEC(#SQL)
..a slightly simpler approach
ALTER PROCEDURE [api].[GetUser_NetId]
#NetId uniqueidentifier
, #LastName nvarchar(200) = '' OUTPUT
, #FirstName nvarchar(200) = '' OUTPUT
, #Country uniqueidentifier = NULL OUTPUT
, #Newsletter bit = 0 OUTPUT
AS
BEGIN
IF EXISTS(SELECT * FROM dbo.Contact WHERE Net_ID = #NetId)
BEGIN
SELECT
#LastName = tbl.LastName,
#FirstName = tbl.FirstName,
#Country = tbl.Address1CountryId,
#Newsletter = tbl.Newsletter
FROM dbo.Contact WHERE Net_ID = #NetId;
END
ELSE
BEGIN
SELECT
#LastName = tbl.LastName,
#FirstName = tbl.FirstName,
#Country = tbl.Address1CountryId,
#Newsletter = tbl.Newsletter
FROM dbo.Lead WHERE Net_ID = #NetId;
END
END
I am getting an error like:
Msg 137, Level 15, State 2, Line 18
Must declare the scalar variable "#CustomerKey".
Msg 137, Level 15, State 1, Line 21
Must declare the scalar variable "#FirstName".
Msg 137, Level 15, State 1, Line 30
Must declare the scalar variable "#FirstName".
IF EXISTS(SELECT * FROM VW_FactInternetSales WHERE CustomerKey = #CustomerKey)
BEGIN
SELECT
#FirstName = .FirstName,
#TaxAmt = .TaxAmt,
#Country = .Country,
#CustomerKey = .CustomerKey
FROM DimCustomer WHERE CustomerKey = #CustomerKey
END
ELSE
BEGIN
SELECT
#FirstName = .FirstName,
#TaxAmt = .TaxAmt,
#Country = .Country,
#CustomerKey = .CustomerKey
FROM VW_FactInternetSales WHERE CustomerKey = #CustomerKey
END
END
I cant add my table in this line
#FirstName = .FirstName,
#TaxAmt = .TaxAmt,
#Country = .Country,
#CustomerKey = .CustomerKey

Select parameters if a condition is fulfilled

I want to select particular elements if my condition is fulfilled. Please, look below at my code. How can I select the data in CASE clause? My way does not work
CREATE PROCEDURE [dbo].[GetUsers]
#firstName varchar(100) = '',
#isCounterSelected int,
#countries dbo.tvp_stringArray READONLY
AS
BEGIN
SET NOCOUNT ON;
SELECT [PK_UserID] AS Id
,[UserNickName]
,[description]
,CASE
WHEN #isCounterSelected = 1 THEN (NULL AS MCount, NULL AS GCount, NULL AS CCount)
ELSE ''
END
FROM [dbo].[User_Existing]
WHERE (#firstName NOT LIKE '' AND firstName LIKE #firstName + '%')
AND Country IN (SELECT inputValue FROM #countries)
I think this would be with syntax errors fixed:
CREATE PROCEDURE [dbo].[GetUsers]
#firstName varchar(100) = '',
#isCounterSelected int,
#countries dbo.tvp_stringArray READONLY
AS
BEGIN
SET NOCOUNT ON;
SELECT [PK_UserID] AS Id
,[UserNickName]
,[description]
,CASE
WHEN #isCounterSelected = 1 THEN NULL
ELSE ''
END AS MCount
,CASE
WHEN #isCounterSelected = 1 THEN NULL
ELSE ''
END AS GCount
,CASE
WHEN #isCounterSelected = 1 THEN NULL AS CCount
ELSE ''
END AS CCount
FROM [dbo].[User_Existing]
WHERE (#firstName NOT LIKE '' AND firstName LIKE #firstName + '%')
AND Country IN (SELECT inputValue FROM #countries)
END

SQL Server 2008 R2: Prepare Dynamic WHERE Clause

I have the following stored procedure with four parameters.
Stored procedure spTest:
CREATE PROCEDURE spTest
#Name varchar(20) = '',
#Address varchar(100) = '',
#City varchar(50) = '',
#Pin varchar(50) = ''
AS
DECLARE #DynamicWhere varchar(max)
DECLARE #Query varchar(max)
/* Here I want to prepare a dynamic where clause for all possibilities */
SET #Query = 'SELECT * FROM Test_Table '+ #DynamicWhere +'';
EXECUTE(#Query);
GO
Well I am preparing it like this:
IF #Name = '' AND #Address = '' AND #City = '' AND #Pin = ''
BEGIN
SET #DynamicWhere = '';
END
ELSE IF #Name != '' AND #Address = '' AND #City = '' AND #Pin = ''
BEGIN
SET #DynamicWhere = 'Name ='''+#Name+'''';
END
ELSE IF #Name != '' AND #Address != '' AND #City = '' AND #Pin = ''
BEGIN
SET #DynamicWhere = 'Name ='''+#Name+''' AND Address ='''+#Address+'''';
END
......
......
Many possibilities
Is this a right way OR is there any better way to prepare the dynamic WHERE clause?
It's called catch-all queries and it basically goes like this:
CREATE PROCEDURE spTest
#Name varchar(20) = '',
#Address varchar(100) = '',
#City varchar(50) = '',
#Pin varchar(50) = ''
AS
SELECT *
FROM Test_Table
WHERE (#Name = '' OR Name = #Name)
AND (#Address = '' OR Address = #Address)
AND (#City = '' OR City = #City)
AND (#Pin = '' OR Pin = #Pin);
GO
You also might want to read this article about catch all queries
You can use ISNULL and NULLIF also in this case:
below code should work :
CREATE PROCEDURE spTest
#Name varchar(20) = '',
#Address varchar(100) = '',
#City varchar(50) = '',
#Pin varchar(50) = ''
AS
SET #Name=NULLIF(#Name,'')
SET #Address=NULLIF(#Address,'')
SET #City=NULLIF(#City,'')
SET #Pin=NULLIF(#Pin,'')
SELECT *
FROM Test_Table
WHERE Name = ISNULL(#Name,Name)
AND Address = ISNULL(#Address,Address)
AND City = ISNULL(#City,City)
AND Pin = ISNULL(#Pin,Pin)
GO
I update the #Zohar answer. Define blank is bad habits, ideally define with null and later use blank. So the query will be
CREATE PROCEDURE spTest
#Name varchar(20) = null,
#Address varchar(100) = null,
#City varchar(50) = null,
#Pin varchar(50) = null
AS
SELECT *
FROM Test_Table
WHERE (Name = ISNULL(#Name,'') )
AND (Address = ISNULL(#Address,''))
AND (City = ISNULL(#City,''))
AND (Pin = ISNULL(#Pin,''));
GO
Even I like the #Biswa answer as it use current version of sql server, but Sqlserver 2008R2 does not have this function.

How to add generic condition to sp select?

I really don't know what to do in this situation, so don't be too harsh.
If I have my select:
declare #Id uniqueidentifier = 'some parent guid'
declare #Type int = 1 -- can be 1, 2 or 3
declare #UserType varchar(max) --can be 0, anything else than 0, or all users at once
if(#Type = 1)
set #UserType = 'and UserType <> 0'
if(#Type = 2)
set #UserType = 'and UserType = 0'
if(#Type = 3)
set #UserType = ''
select * from users where parentId = #Id + #UserType
What to do in the situation where condition is "generic"? Do i really need to create 3 different Sp?
You can use AND/OR logic to simulate the If-else condition in where clause. Try something like this
select * from users
where
parentid= #id
and
(
(#Type = 1 and UserType <> 0)
or
(#Type = 2 and UserType = 0)
or
(#Type = 3)
)
or you can also use Dynamic sql to do this
declare #Id uniqueidentifier = 'some parent guid'
declare #Type int = 1 -- can be 1, 2 or 3
Declare #UserType varchar(max) --can be 0, anything else than 0, or all users at once
Declare #sql nvarchar(max)
if(#Type = 1)
set #UserType = ' and UserType <> 0'
if(#Type = 2)
set #UserType = ' and UserType = 0'
if(#Type = 3)
set #UserType = ''
set #sql = 'select * from users where parentId ='''+ cast(#Id as varchar(25))+''''+ #UserType
--Print #sql
Exec sp_executesql #sql
Different select statements would be most efficient since each is a fundamentally different query. If static SQL becomes unwieldly, use dynamic SQL. Below is a parameterized example using techniques from http://www.sommarskog.se/dyn-search.html.
declare #sql nvarchar(MAX) = N'select * from users where parentId = #Id';
declare #Id uniqueidentifier = 'some parent guid';
declare #Type int = 1; -- can be 1, 2 or 3
declare #UserType varchar(max); --can be 0, anything else than 0, or all users at once
SET #sql = #sql + CASE #Type
WHEN 1 THEN N' and UserType <> 0'
WHEN 2 THEN N' and UserType = 0'
WHEN 3 THEN N'' END;
EXEC sp_executesql
#sql
, N'#Id uniqueidentifier'
, #Id = #Id;

Use If between the where clause

I don't know how exactly I should frame my Question as even I don't know what exactly I have to search. May be this is the best place to put my question. I want to use If condition in where clause this can be done using case but my problem is I don't want to use it after a column name instead I want to avoid entire execution of that column. You can understand it by going through my procedure in the last I have commented, So I want to achieve something like that as I want to write the same procedure again for that condition which i think can be achievable by doing something like that. I searched on internet what I got is to use case in where which doesn't satisfy my purpose. Please look the last lines to understand the problem. Thanx
USE [overseascrm]
GO
/****** Object: StoredProcedure [dbo].[candidatesearch] Script Date: 05-07-2014 00:48:17 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[candidatesearch]
#candidate_id varchar(50),
#firstname varchar(50),
#lastname varchar(50),
#emailid_1 varchar(100),
#mobile_1 varchar(20),
#from varchar(50),
#to varchar(50),
#agentid varchar(50),
#passportno varchar(50),
#profession int,
#isactive bit
as
begin
DECLARE #candidateid_var varchar(50)
DECLARE #firstname_var varchar(50)
DECLARE #lastname_var varchar(50)
DECLARE #emailid1_var varchar(100)
DECLARE #mobile_1_var varchar(20)
DECLARE #agentid_var varchar(50)
IF(#agentid = '')
begin
SET #agentid_var = '%'
END ELSE BEGIN
SET #agentid_var = #agentid
END
IF (#candidate_id = '') BEGIN
SET #candidateid_var = '%'
END ELSE BEGIN
SET #candidateid_var = #candidate_id
END
IF (#firstname = '') BEGIN
SET #firstname_var = '%'
END ELSE BEGIN
SET #firstname_var = #firstname
END
IF (#lastname = '') BEGIN
SET #lastname_var = '%'
END ELSE BEGIN
SET #lastname_var = #lastname
END
IF (#emailid_1 = '') BEGIN
SET #emailid1_var = '%'
END ELSE BEGIN
SET #emailid1_var = #emailid_1
END
IF (#mobile_1 = '') BEGIN
SET #mobile_1_var = '%'
END ELSE BEGIN
SET #mobile_1_var = #mobile_1
END
IF (#from = '') BEGIN
SELECT
*
FROM candidate C
LEFT JOIN candidate_profession_map CM
ON C.candidate_id = CM.candidate_id
LEFT JOIN passport_details PD
ON C.candidate_id = PD.candidate_id
WHERE C.candidate_id LIKE '' + #candidateid_var + '%'
AND firstname LIKE '' + #firstname_var + '%'
AND lastname LIKE '' + #lastname_var + '%'
AND emailid_1 LIKE '' + #emailid1_var + '%'
AND mobile_1 LIKE '' + #mobile_1_var + '%'
AND agent_id LIKE '' + #agentid_var + '%'
AND CM.profession_id = #profession
AND C.isactive = #isactive
AND PD.passport_no = #passportno
END ELSE BEGIN
SELECT
*
FROM candidate C
LEFT JOIN candidate_profession_map CM
ON C.candidate_id = CM.candidate_id
LEFT JOIN passport_details PD
ON C.candidate_id = PD.candidate_id
WHERE C.candidate_id LIKE '' + #candidateid_var + '%'
AND firstname LIKE '' + #firstname_var + '%'
AND lastname LIKE '' + #lastname_var + '%'
AND emailid_1 LIKE '' + #emailid1_var + '%'
AND mobile_1 LIKE '' + #mobile_1_var + '%'
AND agent_id LIKE '' + #agentid_var + '%'
AND C.addedon BETWEEN CONVERT(DATETIME, #from, 103) AND CONVERT(DATETIME, #to, 103)
--IF (#profession <> 0)BEGIN
--AND CM.profession_id = #profession
--END
AND C.isactive = #isactive
OR PD.passport_no = #passportno
END
END
You could use dynamic sql execution where you create your select statement as varchar to your liking, executing it at the end using sp_executesql
http://msdn.microsoft.com/en-us/library/ms188001.aspx
ALTER procedure [dbo].[candidatesearch]
#candidate_id varchar(50),
#firstname varchar(50),
#lastname varchar(50),
#emailid_1 varchar(100),
#mobile_1 varchar(20),
#from varchar(50),
#to varchar(50),
#agentid varchar(50),
#passportno varchar(50),
#profession int,
#isactive bit
AS Begin
SELECT *
FROM candidate C
LEFT JOIN candidate_profession_map CM ON C.candidate_id = CM.candidate_id
LEFT JOIN passport_details PD ON C.candidate_id = PD.candidate_id
WHERE (#candidateid='' or C.candidate_id LIKE #candidateid + '%')
AND (#firstname = '' or firstname LIKE #firstname + '%')
AND (#lastname = '' or lastname LIKE #lastname + '%')
AND (#emailid_1='' or emailid_1 LIKE #emailid1 + '%')
AND (#mobile_1 = '' or mobile_1 LIKE #mobile_1 + '%')
AND (#agentid='' agent_id LIKE #agentid + '%')
AND C.isactive = #isactive
And ((#from=''
AND CM.profession_id = #profession
AND PD.passport_no = #passportno)
or (#from<>''
And C.addedon BETWEEN CONVERT(DATETIME, #from, 103) AND CONVERT(DATETIME, #to, 103)
OR PD.passport_no = #passportno)
End