I want to build custom Where condition based on stored procedure inputs, if not null then I will use them in the statement, else I will not use them.
if #Vendor_Name is not null
begin
set #where += 'Upper(vendors.VENDOR_NAME) LIKE "%"+ UPPER(#Vendor_Name) +"%"'
end
else if #Entity is not null
begin
set #where += 'AND headers.ORG_ID = #Entity'
end
select * from table_name where #where
But I get this error
An expression of non-boolean type specified in a context where a condition is expected, near 'set'.
Use this :
Declare #Where NVARCHAR(MAX)
...... Create your Where
DECLARE #Command NVARCHAR(MAX)
Set #Command = 'Select * From SEM.tblMeasureCatalog AS MC ' ;
If( #Where <> '' )
Set #Comand = #Command + ' Where ' + #Where
Execute SP_ExecuteSQL #Command
I tested this and it Worked
You cannot simply put your variable in normal SQL as you have in this line:
select * from table_name where #where;
You need to use dynamic SQL. So you might have something like:
DECLARE #SQL NVARCHAR(MAX) = 'SELECT * FROM Table_Name WHERE 1 = 1 ';
DECLARE #Params NVARCHAR(MAX) = '';
IF #Vendor_Name IS NOT NULL
BEGIN
SET #SQL += ' AND UPPER(vendors.VENDOR_NAME) LIKE ''%'' + UPPER(#VendorNameParam) + ''%''';
END
ELSE IF #Entity IS NOT NULL
BEGIN
SET #SQL += ' AND headers.ORG_ID = #EntityParam';
END;
EXECUTE SP_EXECUTESQL #SQL, N'#VendorNameParam VARCHAR(50), #EntityParam INT',
#VendorNameParam = #Vendor_Name, #EntityParam = #Entity;
I assume your actual problem is more complex and you have simplified it for this, but if all your predicates are added using IF .. ELSE IF.. ELSE IF, then you don't need dynamic SQL at all, you could just use:
IF #Vendor_Name IS NOT NULL
BEGIN
SELECT *
FROM Table_Name
WHERE UPPER(vendors.VENDOR_NAME) LIKE '%' + UPPER(#Vendor_Name) + '%';
END
ELSE IF #Entity IS NOT NULL
BEGIN
SELECT *
FROM Table_Name
WHERE headers.ORG_ID = #Entity;
END
old question but want to add that i just found using
where
CASE WHEN #id = 0
THEN 1
ELSE id
END
=
CASE WHEN #id = 0
THEN 1
ELSE #id
END
this is from these steps:
where id = #id
check if #id is null (or invalid) or have valid value so replace #id using case:
where id =
CASE WHEN #id is null
THEN 0
ELSE #id
END
this is will end up as
where id = 0 -- if #id is null, still affected your query
where id = #id -- if #id have valid value
because we want use this "where" clause only if have valid data then we change id too
where
CASE WHEN #id is null
THEN 0
ELSE id
END
=
CASE WHEN #id is null
THEN 0
ELSE #id
END
then will end up as
where 0 = 0 -- if #id is null and it doesn't affect your query
where id = #id -- if #id have valid value
don't forget if id is varchar, use 0 as '0'
CASE WHEN #id is null
THEN '0'
ELSE id
END
i use this in my project and working well, even i use more than 1 of this in one query. please let me know if this query costing time at larger data
No need to use ad-hoc query (execute SP_ExecuteSQL)
Check below logic, you can use N number of dynamic / un-sure parameters / conditions
-- flages
declare #chk_vendor bit;
declare #chk_entity bit;
-- setting off
set #chk_entity = 0;
set #chk_vendor = 0;
if #Vendor_Name is not null
begin
set #chk_vendor = 1;
end
else if #Entity is not null
begin
set #chk_entity = 1;
end
SELECT *
FROM table_name
WHERE (UPPER(vendors.VENDOR_NAME) LIKE '%' +
UPPER(#Vendor_Name) + '%' OR #chk_vendor = 0)
AND (headers.ORG_ID = #Entity OR #chk_entity = 0)
I am posting this answer as there many similar questions , remember flag will just enable or disable a condition
Related
have the following table
table TableAB(
ColumnA int,
ColumnB nvarchar(50)
)
i need to create a procedure which returns ColumnA, ColumnB and takes two parameters (in real life count of parameters more then 20). All searches should work with wildcard "%".
procedure has input parameters
#ColumnA int,
#ColumnB nvarchar(50)
i have two approaches
1.
select ColumnA,ColumnB from TableAB
where
ColumnA like
case #ColumnA
when #ColumnA NULL then ColumnA
else '%' + CONVERT(varchar(10),#ColumnA)+'%'
end
and
ColumnB like
case #ColumnB
when #ColumnB NULL then ColumnB
else '%' + ColumnB +'%'
end
2.
DECLARE #TabWhereConditions TABLE(Id INT IDENTITY(1,1), Condition VARCHAR(MAX))
...
SET #ParamDefenition = '#pColumnA int, #pColumnB nvarchar(50)'
IF(NOT #ColumnA IS NULL)
BEGIN
INSERT INTO #TabWhereConditions(Condition)
VALUES('ColumnA like ' + '''%' + CONVERT(varchar(10),#ColumnA) + '%''')
END
IF(NOT #ColumnB IS NULL)
BEGIN
INSERT INTO #TabWhereConditions(Condition)
VALUES('ColumnA like ' + '''%' + #ColumnB + '%''')
END
DECLARE CondCursor CURSOR FOR
SELECT Condition FROM #TabWhereConditions
OPEN CondCursor
SET #WhereString = ''
FETCH NEXT FROM CondCursor INTO #WhereCondition
WHILE ##FETCH_STATUS = 0
BEGIN
SET #WhereString = #WhereString + #WhereCondition + ' AND '
FETCH NEXT FROM CondCursor INTO #WhereCondition
END
CLOSE CondCursor
DEALLOCATE CondCursor
SET #WhereString = SUBSTRING(#WhereString,1, LEN(#WhereString)-4)
SET #SqlCommand = '
SELECT
ColumnA,
ColumnB
FROM TableAB
WHERE ' + #WhereString
EXECUTE sp_executesql #SqlCommand, #ParamDefenition,
#pColumnA = #ColumnA,
#pColumnB = #ColumnB,
Which approach is better ? first or second , or your suggestion
Note: i need solution for situation when procedure can take from 1 up to 20 parameters, each call can get different number of parameters
I believe you want to use something along these lines
select ColumnA,ColumnB from TableAB
where (#columnA is null or ColumnA like '%'+CONVERT(varchar(10),#ColumnA)+'%' and
(#columnB is null or ColumnB like '%'+CONVERT(varchar(10),#ColumnB)+'%'
As noted in this Aaron Bertrand blog post, it is a good idea to make it dynamic T-SQL. Therefore, you may use sys.sp_executesql to run the above statement.
You can create execute stored procedure or query as shown below in case of multiple where condition.
CREATE PROCEDURE [dbo].[SearchUserPermission]
#UserId bigint,
#ParentModuleId Bigint
AS
BEGIN
SET NOCOUNT ON
Declare #strSQLQuery AS VARCHAR(3000) =null;
Declare #strCriteria AS VARCHAR(1000)=null;
BEGIN
SET #strCriteria = ' WHERE 1=1'
IF(#UserId > 0)
BEGIN
SET #strCriteria=#strCriteria + ' AND UserId =' + CONVERT(VARCHAR,#UserId)
END
IF( #ParentModuleId > 0)
BEGIN
SET #strCriteria=#strCriteria + ' AND ParentModuleId=' + Convert(varchar,#ParentModuleId)
END
SET #strSQLQuery ='Select * From (
SELECT
*
FROM Module_Mst
LEFT outer join UserPermissions_Mst
on UserPermissions_Mst.ModuleId=Module_Mst.moduleId AND Module_Mst.IsVisible=1
' +#strCriteria + ' ) AS TempUserPermission'
if (LEN(#strCriteria) > 0 )
BEGIN
SET #strSQLQuery = #strSQLQuery + ' ORDER By OrderNo '
END
END
--Print(#strSQLQuery)
EXEC (#strSQLQuery)
END
Hope this will help you.
I want to set a variable to the value generated using the dynamic query outside the query.
I tried the sp_executesql concept, but this does not help me because I am using the parameter value in the dynamic query.
Are there any possibilities to fix this issue?
CREATE PROCEDURE [dbo].[SP_test_proc]
#id int = null,
#deptId int = null
As
BEGIN
DECLARE #Condition VARCHAR(MAX),#Query NVARCHAR(MAX)
SET #Condition= 'Where Id = '#id + case when #deptid is null then '' else #deptid End
SET #Query = 'Declare #Name varchar(100)
Set #Name = Select name from student '+ #Condition
SELECT *
FROM personal
WHERE name = #Name
END
Use output parameter in dynamic sql as shown here
Also see this
Try this :
DECLARE #Condition VARCHAR(MAX),#Query NVARCHAR(MAX)
SET #Condition= 'Where Id = '#id + case when #deptid is null then '' else #deptid End
Declare #Name varchar(100),#nameval varchar(100),#paramdef varchar(100)
SET #Query = '
Select #Name = name from student '+ #Condition
set #paramdef=N'#Name varhcar(20) OUTPUT'
execute sp_executesql #Query,#paramdef,#Name=#nameval Output
SELECT *
FROM personal
WHERE name = #nameval
Hope this code will work for you.
CREATE PROCEDURE [dbo].[SP_test_proc]
#id int = null,
#deptId int = null
As
BEGIN
DECLARE #Condition VARCHAR(MAX),#Query NVARCHAR(MAX)
SET #Condition= 'Where Id = ' + #id + ' And [Deptid] = ' + ISNULL(#deptid,'')
SET #Query = 'SELECT * FROM personal WHERE name IN ( SELECT name FROM student ' + #Condition + ')'
EXEC #Query
END
[Deptid] is not sure I don't know column name
I am trying to create optional parameters in a stored process in which I group by the parameters under certain conditions.
For example:
SELECT
TP.ProductID,
case
when #passangers='Y' then (TP.Passangersgroup)
when #fareclass='Y' then (TP.Fareclass)
when #ispriorbooking='Y' then (TP.IsPriorBooking)
end
INTO ##B
FROM ##A TP
GROUP BY
TP.ProductID,
case
when #passangers='Y' then (TP.Passangersgroup)
when #fareclass='Y' then (TP.Fareclass)
when #ispriorbooking='Y' then (TP.IsPriorBooking)
end
In this case, I would be able to select 'Y' for any of the 3 parameters, and I would want to add them to select statement and group by.
Any ideas?
You need to do this with dynamic SQL; something like:
declare #sql varchar(max) = 'SELECT
TP.ProductID, ' +
case when #passangers='Y' then 'TP.Passangersgroup'
when #fareclass='Y' then 'TP.Fareclass'
when #ispriorbooking='Y' then 'TP.IsPriorBooking'
else ''
end
+ ' INTO ##B
FROM ##A TP'
--ETC
Exec(#sql)
If you want to add up to all three columns, you need three case statements:
declare #sql varchar(max) = 'SELECT
TP.ProductID, ' +
case when #passangers='Y' then 'TP.Passangersgroup' else '' end
+ case when #fareclass='Y' then 'TP.Fareclass' else '' end
--ETC.
+ ' INTO ##B
FROM ##A TP'
Dynamic SQL will be the best bet, but I would figure out the column you want and then pass in the one column as a variable. Less likely to suffer SQL injection and more readable.
DECLARE #passangers CHAR(1), #fareclass CHAR(1), #ispriorbooking CHAR(1)
SET #passangers='Y'
DECLARE #SQLCMD NVARCHAR(MAX), #YValue NVARCHAR(1000)
--set the select and group by field
SELECT #YValue=
case
when #passangers='Y' then N'TP.Passangersgroup'
when #fareclass='Y' then N'TP.Fareclass'
when #ispriorbooking='Y' then N'TP.IsPriorBooking'
else NULL
end
IF #YValue IS NOT NULL
BEGIN
SET #SQLCMD=N'
SELECT
TP.ProductID,'+#YValue+'
INTO ##B
FROM ##A TP
GROUP BY
TP.ProductID, '+#YValue
PRINT #SQLCMD
--EXEC sp_executesql #SQLCMD
END
ELSE
PRINT 'INVALID PARAMETER PASSED IN'
You have to use dynamic sql but case statement mentioned in Steve Mangiameli code will not work in case when more than one column is selected as 'Y'. The below code will be working fine for multiple columns selected as 'Y'-
create procedure proc1
#passangers varchar(100) = null,
#fareclass varchar(100) = null,
#ispriorbooking varchar(100) = null
as
begin
declare #sql nvarchar(100)
declare #var varchar(100)
if #passangers = 'y'
set #var = tp.Passangersgroup + ', '
if #fareclass = 'y'
set #var = #var + TP.Fareclass + ', '
if #ispriorbooking = 'y'
set #var = #var + TP.IsPriorBooking
set #sql = 'select ' + #var + ' into ##b from ##a as TP group by ' + #var + 'option(recomplile)'
exec sp_executesql #sql
end
This week, I found myself in need of some dynamic queries. Now, dynamic queries and dynamic where clauses are nothing new and well documented all over the web. Yet, I needed something more. I needed a fluid way of pulling new where fields to the client and allowing the users to make as many filters as needed. Even have multiple filters on a single field. Even more so, I needed to have access to all the possible operators within SQL server. The following is code is one way to make this happen. I will attempt to point out highlights of the code with the complete code at the bottom.
Hope you enjoy the code.
REQUIREMENTS
The solution will never allow SQL injections. (No exec(command) can be used)
The caller of the stored procedure could be anything.
The data set must come from a Stored Procedure.
Any field can be filtered as many times as needed, with just about any operation.
Any combination of filters should be allowed.
The stored procedure should allow for mandatory parameters
First, let us look over the parameters.
CREATE PROCEDURE [dbo].[MyReport]
-- Add the parameters for the stored procedure here
#p_iDistributorUID INT, -- manditory
#p_xParameters XML = null --optional parameters (hostile)
The first parameter must always be sent, in this demo we have a distributor id that must be sent in. The second parameter is an XML document. These are the “Dynamic Where Clauses” and we consider these potential sql injections, or as I perceive this parameter as hostile.
<root>
<OrFilters>
<AndFilter Name="vcinvoicenumber" Operator="2" Value="inv12"/>
<AndFilter Name="vcID" Operator="1" Value="asdqwe"/>
</OrFilters>
<OrFilters>
<AndFilter Name="iSerialNumber" Operator="1" Value="123456"/>
</OrFilters>
NAME= field name(you could just use the object_id if you want to obfuscate)
OPERATOR = SQL operators such as <,>,=,like,ect.
VALUE is what the users has entered.
Here is what the final code would look like.
Select *
FROM someTable
Where (
vcinvoicenumber like ‘inv12%’
and vcID = ‘asdqwe’
)
Or
(
iSerialNumber = ‘123456’
)
First thing is to find out how many “OrFilters” tags there are.
SELECT #l_OrFilters = COUNT(1)
FROM #p_xParameters.nodes('/root/OrFilters') Tab(Col)
Next we need a temp table to hold the values in the XML doc.
CREATE TABLE #temp
(keyid int IDENTITY(1,1) NOT NULL,value varchar(max))
We now create a cursor for the first “OrFilters”tag.
DECLARE OrFilter_cursor CURSOR LOCAL
FOR
SELECT Tab.Col.value('#Name','varchar(max)') AS Name
,Tab.Col.value('#Operator','Smallint') AS Operator
,Tab.Col.value('#Value','varchar(max)') AS Value
FROM #p_xParameters.nodes('/root/OrFilters[sql:variable("#l_OrFilters")]/AndFilter') Tab(Col)
To make sure we have a valid field, we check against the system tables.
SELECT #l_ParameterInName = [all_columns].Name
,#l_ParameterDataType= [systypes].Name
,#l_ParameterIsVariable= Variable
,#l_ParameterMax_length=max_length
,#l_ParameterpPrecision=precision
,#l_ParameterScale =[all_columns].scale
FROM [AprDesktop].[sys].[all_views]
INNER JOIN [AprDesktop].[sys].[all_columns]
ON [all_views].object_id = [all_columns].object_id
INNER JOIN [AprDesktop].[sys].[systypes]
ON [all_columns].system_type_id = [systypes].xtype
WHERE [all_views].name = 'vw_CreditMemo_Lists'
and [all_columns].Name= #l_Name
Now we save the parameter to the temp table
IF ##ROWCOUNT = 1
BEGIN
INSERT INTO #temp (value) SELECT #l_Value
SET #l_FilterKey = ##IDENTITY
.
.
.
We make a call to a function that will actually build the where clauses.
SET #l_TemporaryWhere +=
dbo.sfunc_FilterWhereBuilder2(#l_Operator
,#l_ParameterInName
,#l_TemporaryWhere
,CAST(#l_FilterKey AS VARCHAR(10))
,#l_ParameterDataType
,#l_ParameterVariable)
Looking at this Function, you can see we used a case statement to genereate the where clause string.
set #l_CastToType = ' CAST( VALUE as ' + #p_DataType + #p_PrecisionScale + ') '
set #l_CastToString = ' CAST( '+#p_Field+' as VARCHAR(MAX)) '
-- Add the T-SQL statements to compute the return value here
SELECT #l_Return =
CASE
--EQUAL
--ex: vcUID = (select value FROM #temp where keyid = 1)
WHEN #p_Command = 1
THEN #p_Field + ' = (select '+#l_CastToType+' FROM #temp where keyid = ' + #p_KeyValue + ')'
--BEGIN WITH
--ex:vcInvoiceNumber LIKE (select value+'%' FROM #temp where keyid = 2)
WHEN #p_Command = 2
THEN #l_CastToString +' LIKE (select value+'+ QUOTENAME('%','''') +' FROM #temp where keyid = ' + #p_KeyValue + ')'
.
.
.
And finally call the sp_execute.
EXECUTE sp_executesql #l_SqlCommand ,#l_Parameters, #p_iDistributorUID
CALLING CODE
DECLARE #return_value int
DECLARE #myDoc xml
SET #myDoc =
'<root>
<OrFilters>
<AndFilter Name="vcinvoicenumber" Operator="1" Value="123"/>
</OrFilters>
</root>'
EXEC #return_value = [dbo].[spp_CreditMemo_Request_List_v2]
#p_siShowView = 1,
#p_iDistributorUID = 3667,
#p_xParameters = #myDoc
SELECT 'Return Value' = #return_value
MAIN STORED PROCEDURE
ALTER PROCEDURE [dbo].[MyReport]
-- Add the parameters for the stored procedure here
#p_iDistributorUID INT , --manditory
#p_xParameters XML = null --optional parameters(hostile)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #l_TemporaryWhere NVARCHAR(MAX)
-- declare variables
DECLARE #l_SqlCommand NVARCHAR(MAX)
DECLARE #l_Parameters NVARCHAR(MAX)
DECLARE #l_WhereClause NVARCHAR(MAX)
DECLARE #l_OrFilters INT
--cursor variables
DECLARE #l_Name VARCHAR(MAX)
DECLARE #l_Operator SMALLINT
DECLARE #l_Value VARCHAR(MAX)
--variables from the database views
DECLARE #l_ParameterInName NVARCHAR(128)
DECLARE #l_ParameterDataType NVARCHAR(128)
DECLARE #l_ParameterIsVariable BIT
DECLARE #l_ParameterMax_length SMALLINT
DECLARE #l_ParameterpPrecision TINYINT
DECLARE #l_ParameterScale TINYINT
--the variable that holds the latest ##identity
DECLARE #l_FilterKey INT
--init local variables
SET #l_SqlCommand =''
SET #l_Parameters =''
SET #l_WhereClause =''
BEGIN TRY
--verify manditory variables
if #p_iDistributorUID is null
raiserror('Null values not allowed for #p_iDistributorUID', 16, 1)
--Build the base query
-- only the fields needed in the tile should be selected
SET #l_SqlCommand =
' SELECT * ' +
' FROM vw_Lists '
--how many "OR" filters are there
SELECT #l_OrFilters = COUNT(1)
FROM #p_xParameters.nodes('/root/OrFilters') Tab(Col)
--create a temp table to
--hold the parameters to send into the sp
CREATE TABLE #temp
(
keyid int IDENTITY(1,1) NOT NULL,value varchar(max)
)
--Cycle through all the "OR" Filters
WHILE #l_OrFilters > 0
BEGIN
SET #l_TemporaryWhere = '';
--Create a cursor of the Next "OR" filter
DECLARE OrFilter_cursor CURSOR LOCAL
FOR
SELECT Tab.Col.value('#Name','varchar(max)') AS Name
,Tab.Col.value('#Operator','Smallint') AS Operator
,Tab.Col.value('#Value','varchar(max)') AS Value
FROM #p_xParameters.nodes('/root/OrFilters[sql:variable("#l_OrFilters")]/AndFilter') Tab(Col)
OPEN OrFilter_cursor
FETCH NEXT FROM OrFilter_cursor
INTO #l_Name, #l_Operator,#l_Value
WHILE ##FETCH_STATUS = 0
BEGIN
--verify the parameter actual exists
-- and get parameter details
SELECT #l_ParameterInName = [all_columns].Name
,#l_ParameterDataType= [systypes].Name
,#l_ParameterIsVariable= Variable
,#l_ParameterMax_length=max_length
,#l_ParameterpPrecision=precision
,#l_ParameterScale =[all_columns].scale
FROM [AprDesktop].[sys].[all_views]
INNER JOIN [sys].[all_columns]
ON [all_views].object_id = [all_columns].object_id
INNER JOIN [sys].[systypes]
ON [all_columns].system_type_id = [systypes].xtype
WHERE [all_views].name = 'vw_CreditMemo_Lists'
and [all_columns].Name= #l_Name
--if the paremeter exists, create a where clause
-- if the parameters does not exists, possible injection
IF ##ROWCOUNT = 1
BEGIN
--insert into the temp table the parameter value
--NOTE: we have turned in the ##identity as the key
INSERT INTO #temp (value) SELECT #l_Value
SET #l_FilterKey = ##IDENTITY
-- if the parameter is variable in length, add the length
DECLARE #l_ParameterVariable VARCHAR(1000)
IF #l_ParameterIsVariable = 1
BEGIN
SET #l_ParameterVariable ='(' + CAST(#l_ParameterMax_length as VARCHAR(MAX)) + ') '
END
ELSE
BEGIN
SET #l_ParameterVariable = ''
END
-- create the where clause for this filter
SET #l_TemporaryWhere +=
dbo.sfunc_FilterWhereBuilder2(#l_Operator
,#l_ParameterInName
,#l_TemporaryWhere
,CAST(#l_FilterKey AS VARCHAR(10))
,#l_ParameterDataType
,#l_ParameterVariable)
END
FETCH NEXT FROM OrFilter_cursor
INTO #l_Name, #l_Operator,#l_Value
END
-- clean up the cursor
CLOSE OrFilter_cursor
DEALLOCATE OrFilter_cursor
--add the and filers
IF #l_TemporaryWhere != ''
BEGIN
--if the where clause is not empty, we need to add an OR
IF #l_WhereClause != ''
BEGIN
SET #l_WhereClause += ' or ';
END
--add temp to where clause including the
SET #l_WhereClause += '(' + #l_TemporaryWhere + ')';
END
--get the next AND set
SET #l_OrFilters = #l_OrFilters - 1
END
--generate the where clause
IF #l_WhereClause != ''
BEGIN
SET #l_WhereClause ='('+ #l_WhereClause + ') AND '
END
--add in the first mandatory parameter
SET #l_WhereClause += ' vw_CreditMemo_Lists.iDistributorUID = #l_iDistributorUID '
SET #l_Parameters += '#l_iDistributorUID int'
--do we need to attach the where clause
if #l_WhereClause IS NOT NULL AND RTRIM(LTRIM(#l_WhereClause)) != ''
BEGIN
SET #l_SqlCommand += ' WHERE '+ #l_WhereClause;
END
print #l_SqlCommand
--query for the data
EXECUTE sp_executesql #l_SqlCommand ,#l_Parameters, #p_iDistributorUID
END TRY
BEGIN CATCH
DECLARE #ErrorUID int;
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
--write the to stored procedure log
EXEC #ErrorUID = spp_Errors_CreateEntry #l_SqlCommand
-- Use RAISERROR inside the CATCH block to return error
-- information about the original error that caused
-- execution to jump to the CATCH block.
RAISERROR (#ErrorUID, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState -- State.
);
IF(CURSOR_STATUS('LOCAL','OrFilter_cursor') >= 0)
BEGIN
CLOSE OrFilter_cursor
END
IF(CURSOR_STATUS('LOCAL','OrFilter_cursor') = -1)
BEGIN
DEALLOCATE OrFilter_cursor
END
END CATCH
return
END
FUNCTION
ALTER FUNCTION [dbo].[sfunc_FilterWhereBuilder2]
(
#p_Command SMALLINT ,
#p_Field VARCHAR(1000) ,
#p_WhereClause VARCHAR(MAX) ,
#p_KeyValue VARCHAR(10) ,
#p_DataType VARCHAR(100) = NULL ,
#p_PrecisionScale VARCHAR(100) = NULL
)
RETURNS VARCHAR(MAX)
AS
BEGIN
-- Declare the return variable here
DECLARE #l_Return VARCHAR(MAX)
DECLARE #l_CastToType VARCHAR(4000)
DECLARE #l_CastToString VARCHAR(MAX)
set #l_CastToType = ' CAST( VALUE as ' + #p_DataType + #p_PrecisionScale + ') '
set #l_CastToString = ' CAST( '+#p_Field+' as VARCHAR(MAX)) '
-- Add the T-SQL statements to compute the return value here
SELECT #l_Return =
CASE
--EQUAL
--ex: vcBurnUID = (select value FROM #temp where keyid = 1)
WHEN #p_Command = 1
THEN #p_Field + ' = (select '+#l_CastToType+' FROM #temp where keyid = ' + #p_KeyValue + ')'
--BEGIN WITH
--ex:vcInvoiceNumber LIKE (select value+'%' FROM #temp where keyid = 2)
WHEN #p_Command = 2
THEN #l_CastToString +' LIKE (select value+'+ QUOTENAME('%','''') +' FROM #temp where keyid = ' + #p_KeyValue + ')'
--END WITH
--ex:vcInvoiceNumber LIKE (select '%'+value FROM #temp where keyid = 2)
WHEN #p_Command = 4
THEN #l_CastToString +' LIKE (select '+ QUOTENAME('%','''') +'+value FROM #temp where keyid = ' + #p_KeyValue + ')'
--END WITH
--ex:vcInvoiceNumber LIKE (select '%'+value+'%' FROM #temp where keyid = 2)
WHEN #p_Command = 8
THEN #l_CastToString +' LIKE (select '+ QUOTENAME('%','''') +'+value+'+ QUOTENAME('%','''') +' FROM #temp where keyid = ' + #p_KeyValue + ')'
--greater than
--ex: iSerialNumber > (select CAST(value as INT) FROM #temp where keyid = 1)
WHEN #p_Command = 16
THEN #p_Field +' > (select '+#l_CastToType+' FROM #temp where keyid = ' + #p_KeyValue + ')'
--greater than equal
--ex: iSerialNumber >= (select CAST(value as INT) FROM #temp where keyid = 1)
WHEN #p_Command = 32
THEN #p_Field +' >= (select '+#l_CastToType+' FROM #temp where keyid = ' + #p_KeyValue + ')'
--Less than
--ex: iSerialNumber < (select CAST(value as INT) FROM #temp where keyid = 1)
WHEN #p_Command = 64
THEN #p_Field +' < (select '+#l_CastToType+' FROM #temp where keyid = ' + #p_KeyValue + ')'
--less than equal
--ex: iSerialNumber <= (select CAST(value as INT) FROM #temp where keyid = 1)
WHEN #p_Command = 128
THEN #p_Field +' <= (select '+#l_CastToType+' FROM #temp where keyid = ' + #p_KeyValue + ')'
--less than equal
--ex: iSerialNumber != (select CAST(value as INT) FROM #temp where keyid = 1)
WHEN #p_Command = 256
THEN #p_Field +' != (select '+#l_CastToType+' FROM #temp where keyid = ' + #p_KeyValue + ')'
--default to an empty string
ELSE ''
END
if #l_Return != '' and LEN(#p_WhereClause) > 1
begin
set #l_Return = ' AND ' + #l_Return
end
-- Return the result of the function
RETURN #l_Return
END
This code in a store procedure worked for years, now on the OPEN line I am getting A cursor with the name ... does not exist.
Did something change in sp_executesql that might have caused this to break?
Is there another way of doing this (the need for dynamic SQL is because the #Types param is passed in as a pre-formatted string for the IN clause?)
Select #Query = 'DECLARE cur_person CURSOR FOR
SELECT *
FROM MyTable
WHERE PersonID = #PersonnelID
AND Type in ' + #Types + ' <== formatted list for IN clause
EXEC sp_executesql #Query
OPEN cur_person <== get cursor doesn't exist error
In your example, that means that the cursor is defined locally.
You can define it globally with database option (CURSOR_DEFAULT) but that might not be a good idea.
Another thing that you can do is put all code in the query and execute it.
I don't know why it fails, but here's a split function that avoids the need for the dynamic query:
CREATE FUNCTION StringToTable
(
#p_list varchar(MAX),
#p_separator varchar(5) = ',',
#p_distinct bit = null
)
RETURNS
#ParsedList table
(
element varchar(500)
)
AS
BEGIN
DECLARE #v_element varchar(500), #Pos int, #v_insert_ind bit
SET #p_list = LTRIM(RTRIM(#p_list))+ #p_separator
SET #Pos = CHARINDEX(#p_separator, #p_list, 1)
IF REPLACE(#p_list, #p_separator, '') <> ''
BEGIN
WHILE #Pos > 0
BEGIN
SET #v_insert_ind = 1
SET #v_element = LTRIM(RTRIM(LEFT(#p_list, #Pos - 1)))
IF #v_element <> ''
BEGIN
IF (#p_distinct = 1)
AND (SELECT count(element) FROM #ParsedList WHERE element = #v_element) > 0
SET #v_insert_ind = 0
IF #v_insert_ind = 1
BEGIN
INSERT INTO #ParsedList (element)
VALUES (#v_element)
END
END
--
SET #p_list = RIGHT(#p_list, LEN(#p_list) - #Pos)
SET #Pos = CHARINDEX(#p_separator, #p_list, 1)
END
END
RETURN
END