I have a small version of my problem in a stored procedure i would like to pass the parameters to as follows:
create procedure VariableTest
#Date1 DATETIME,
#Date2 DATETIME,
#CustomName1 NVARCHAR(100),
#CustomNum2 INT
AS
DECLARE #Condition NVARCHAR(MAX)
DECLARE #SQL NVARCHAR(MAX)
IF(LEN(#CustomName1) > 0)
SET #Condition = 'CustomerName = ' + #CustomName1;
IF(LEN(#CustomNum2) > 0 AND #CustomNum2 > 0)
SET #Condition = 'ClientNumber = ' + #CustomNum2;
SET #SQL =
'SELECT * FROM MyExampleTable ex
WHERE DateEx1 between ' + #Date1 + ' AND ' + #Date2 + '
AND ' + #Condition + '
ORDER BY ex.Sort'
exec #SQL
The problem i am running into, is that when i print the sql the dates do not have the a quotation around them and they are in datetime format, the other issue is that in the condition when the CustomerName is available the customer name comes out without quotes around them as well...
How can i make it so that in the sql string i can include those quotes around the parameters passed?.
OPTED SOLUTION:
create procedure VariableTest
#Date1 DATETIME,
#Date2 DATETIME,
#CustomName1 NVARCHAR(100),
#CustomNum2 INT
AS
DECLARE #Condition NVARCHAR(MAX)
DECLARE #SQL NVARCHAR(MAX)
IF(LEN(#CustomName1) > 0)
SET #Condition = 'CustomerName = #MyVal1';
IF(LEN(#CustomNum2) > 0 AND #CustomNum2 > 0)
SET #Condition = 'ClientNumber = #MyVal2';
DECLARE #MyParams NVARCHAR(200)
SET #MyParams = N'#date1 datetime, #date2 datetime, #MyVal1 NVARCHAR(200), #MyVal2 int';
SET #SQL =
'SELECT * FROM MyExampleTable ex
WHERE DateEx1 between #date1 AND #date2
AND ' + #Condition + '
ORDER BY ex.Sort'
EXECUTE exec sp_executesql
#SQL,
#MyParams,
#date1 = #Date1,
#date2 = #Date2,
#MyVal1 = #CustomName1,
#MyVal2 = #CustomNum2;
Your procedure is prone to sql-injection avoid concatenating parameters and use paramerterised queries like this....
CREATE PROCEDURE VariableTest
#Date1 DATETIME,
#Date2 DATETIME,
#CustomName1 NVARCHAR(100),
#CustomNum2 INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(MAX);
SET #SQL = N'SELECT * FROM MyExampleTable '
+ N' WHERE DateEx1 between #Date1 AND #Date2 '
+ CASE WHEN LEN(#CustomName1) > 0
THEN N' AND CustomerName = #CustomName1 ' ELSE N'' END
+ CASE WHEN LEN(#CustomNum2) > 0 AND #CustomNum2 > 0
THEN N' AND ClientNumber = #CustomNum2 ' ELSE N'' END
+ N' ORDER BY [Sort] '
exec sp_executesql #SQL
,N'#Date1 DATETIME, #Date2 DATETIME
#CustomName1 NVARCHAR(100), #CustomNum2 INT '
,#Date1
,#Date2
,#CustomName1
,#CustomNum2
END
You can do this without dynamic query
SELECT * FROM MyExampleTable ex
WHERE DateEx1 between #Date1 AND #Date2
AND ((LEN(#CustomName1) > 0 AND CustomerName = #CustomName1)
or (LEN(#CustomNum2) > 0 AND #CustomNum2 > 0 and ClientNumber = #CustomNum2))
ORDER BY ex.Sort
Considering that both the condition should be applied in where clause if both the IF clause is satisfied
Dynamic query.
You need to add few more quotes to get the quotes around date
IF( Len(#CustomName1) > 0 )
SET #Condition = 'CustomerName = ''' + #CustomName1 + '';
IF( Len(#CustomNum2) > 0
AND #CustomNum2 > 0 )
SET #Condition = 'ClientNumber = '
+ CONVERT(VARCHAR(50), #CustomNum2);
SET #SQL = 'SELECT * FROM MyExampleTable ex
WHERE DateEx1 between '''
+ CONVERT(VARCHAR(50), #Date1) + ''' AND '''
+ CONVERT(VARCHAR(50), #Date2) + '''
AND '
+ #Condition + '
ORDER BY ex.Sort'
exec(#SQL)
The link Jeroen posted is an excellent resource for using dynamic SQL, and well worth reading (especially for avoiding the aforementioned injection attack).
That said, what you want to use here is the system stored procedure sp_executesql. The idea is you can essentially pass in (and out) variables so you don't have to concatenate them if you don't need to. The quick dirty explanation is you declare an extra variable to hold declarations for the parameters you want to pass in, then you execute sp_executesql indicating the sql text (first parameter), which variable holds your parameter declarations (second parameter) and as many variables as you declared (3rd through nth parameters). Here's the MSDN documentation on sp_executesql:
https://msdn.microsoft.com/en-us/library/ms188001.aspx
Then, your code would look like this instead:
create procedure VariableTest
#Date1 DATETIME,
#Date2 DATETIME,
#CustomName1 NVARCHAR(100),
#CustomNum2 INT
AS
DECLARE #Condition NVARCHAR(MAX)
DECLARE #SQL NVARCHAR(MAX)
declare #parameters nvarchar(2000)
IF(LEN(#CustomName1) > 0)
SET #Condition = 'CustomerName = ' + #CustomName1;
IF(LEN(#CustomNum2) > 0 AND #CustomNum2 > 0)
SET #Condition = 'ClientNumber = ' + #CustomNum2;
SET #SQL =
'SELECT * FROM MyExampleTable ex
WHERE DateEx1 between #Date1 AND #Date2
AND ' + #Condition + '
ORDER BY ex.Sort'
set #parameters = '#date1 datetime, #date2 datetime'
exec sp_executesql
#sql,
#params,
#date1,
#date2
I don't particularly like your opted solution.
NoDisplayName got it right by not using the dynamic query. Using dynamic queries means that you're not going to get the benefits of the SP query plan cache.
The non-dynamic query also has added benefit as it generates compile issues if you create the SP with query errors. The dynamic SQL hides all that.
The only time you'll get performance issues is if you're using a LIKE statement. Values like "%ABC", "AB%C" and "ABC%" all use different query plans. You can safely cache the query plan when using equals.
This is what I'd use:
SELECT *
FROM MyExampleTable ex
WHERE DateEx1 between #Date1 AND #Date2
AND ((LEN(#CustomName1) > 0 AND CustomerName = #CustomName1)
OR (#CustomNum2 > 0 and ClientNumber = #CustomNum2))
ORDER BY ex.Sort
I'd also make the parameter passed NULL if it didn't contain anything.
Related
I'm trying to write a stored procedure in SQL Server that gets the columns as parameters. The user will select the column name from a combo box and will write the searched value for that column on a textbox.
I've been searching how to do this and so far i have this:
ALTER PROCEDURE [dbo].[SP_Select_TBL_Folio]
#cant int,
#Column1 nvarchar(50),
#Value1 nvarchar(50),
#Column2 nvarchar(50),
#Value2 nvarchar(50),
#Column3 nvarchar(50),
#Value3 nvarchar(50)
AS
BEGIN
declare #query nvarchar (max)
SET NOCOUNT ON;
if #cant = 1
BEGIN
set #query = 'SELECT * FROM TBL_Folio WHERE ' + #Column1 + ' LIKE '+ #Value1 + ' ORDER BY 1 DESC';
exec sp_executesql #query, N' '
END
else
BEGIN
if #cant = 2
BEGIN
set #query = 'SELECT * FROM TBL_Folio WHERE ' + #Column1 + ' LIKE '+ #Value1 + ' AND ' + #Column2 + ' LIKE '+ #Value2 + ' ORDER BY 1 DESC';
exec sp_executesql #query, N' '
END
ELSE
if #cant = 3
BEGIN
set #query = 'SELECT * FROM TBL_Folio WHERE ' + #Column1 + ' LIKE '+ #Value1 + ' AND ' + #Column2 + ' LIKE '+ #Value2 + ' AND ' + #Column3 + ' LIKE '+ #Value3 + ' ORDER BY 1 DESC';
exec sp_executesql #query, N' '
END
END
END
The user can send 1 to 3 values, for that I have the parameter #cant, this code works but I want to know if there is a better way to do this or how can I improve this stored procedure.
I think what you have is fine if you need to do it in an SP rather than client side. I would probabably initialize the query to the 'select * from TBL_Folio" and then append the LIKES after each if. I would also caution against using SELECT * so your client side doesn't blow up if a field gets added to the table.
If you have a need to check a variable number of fields rather than just up to 3, you can do a table-valued parameter and build up your query by looping through. Here is an example:
ALTER PROCEDURE [dbo].[GetFilteredInvoices]
#FilterColumns ColumnValueType READONLY
AS
BEGIN
SET NOCOUNT ON;
declare #columnName varchar(50), #columnValue varchar(MAX), #query nvarchar(MAX), #count int
set #query='SELECT InvoiceNumber, InvoiceDate, Customer from Invoices '
set #count=0
set #columnName=''
while exists(select * from #FilterColumns where ColumnName>#ColumnName)
begin
set #columnName=(select min(ColumnName) from #FilterColumns where ColumnName>#columnName)
if #count=0
set #query=#query+'WHERE '
else
set #query=#query+'AND '
set #query=#query+ (select ColumnName+' Like ''%'+ColumnValue+'%'' ' from #filterColumns where ColumnName=#columnName)
set #count=#count+1
end
exec sp_executesql #query
END
Here is the table valued type I used:
CREATE TYPE [dbo].[ColumnValueType] AS TABLE(
[ColumnName] [varchar](50) NULL,
[ColumnValue] [varchar](max) NULL
)
GO
Now this will take any number of columns and values to apply the filter.
Here is an example call to the procedure:
DECLARE #RC int
DECLARE #FilterColumns [dbo].[ColumnValueType]
insert into #filterColumns
Values('InvoiceNumber','345')
,('Customer','67')
EXECUTE #RC = [dbo].[GetFilteredInvoices]
#FilterColumns
I think you can perhaps improve the way that you handle your input parameters by getting rid of the #cant parameter. You can also improve the way that you build up the conditions, at the moment you are not handling the situations where only #Column2 and #Value2 or only #Column3 and #Value3 is set (perhaps it is not needed in your case, but it is still good practice to handle these types of scenarios)
CREATE PROCEDURE SP_Select_TBL_Folio
#Column1 NVARCHAR(50) = NULL,
#Value1 NVARCHAR(50) = NULL,
#Column2 NVARCHAR(50) = NULL,
#Value2 NVARCHAR(50) = NULL,
#Column3 NVARCHAR(50) = NULL,
#Value3 NVARCHAR(50) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE
#P1 NVARCHAR(500),
#P2 NVARCHAR(500),
#P3 NVARCHAR(500),
#SQL NVARCHAR(MAX)
IF (#Column1 IS NULL OR #Column1 = '') AND (#Value1 IS NULL OR #Value1 = '')
BEGIN
-- This will build up dynamic SQL to always select records even if #Column1
-- and #Value1 is not set. This obvisously all depends on your requirements
-- and if you still need to select records if the parameters are not set, otherwise
-- it can be changed to ' WHERE ThePrimaryKeyColumn = 0'
SET #P1 = ' WHERE ThePrimaryKeyColumn > 0'
END
ELSE
BEGIN
SET #P1 = 'WHERE ' + #Column1 + ' LIKE ' + '''' + #Value1 + ''''
END
IF (#Column2 IS NULL OR #Column2 = '') AND (#Value2 IS NULL OR #Value2 = '')
BEGIN
SET #P2 = ''
END
ELSE
BEGIN
SET #P2 = ' AND ' + #Column2 + ' LIKE ' + '''' + #Value2 + ''''
END
IF (#Column3 IS NULL OR #Column3 = '') AND (#Value3 IS NULL OR #Value3 = '')
BEGIN
SET #P3 = ''
END
ELSE
BEGIN
SET #P3 = ' AND ' + #Column3 + ' LIKE ' + '''' + #Value3 + ''''
END
SET #SQL = 'SELECT * FROM TBL_Folio
[P1]
[P2]
[P3]'
-- Here we set all the conditions
SET #SQL = REPLACE(#SQL, '[P1]', #P1);
SET #SQL = REPLACE(#SQL, '[P2]', #P2);
SET #SQL = REPLACE(#SQL, '[P3]', #P3);
-- This will be replaced by EXEC(#SQL)
PRINT #SQL
END
So now you can for instance execute
EXEC SP_Select_TBL_Folio
which will give you
SELECT * FROM TBL_Folio
WHERE ThePrimaryKeyColumn > 0
or you can execute
EXEC SP_Select_TBL_Folio 'Column1','Value1'
which will give you
SELECT * FROM TBL_Folio
WHERE Column1 LIKE 'Value1'
or you can execute
EXEC SP_Select_TBL_Folio NULL,NULL,'Column2','Value2'
which will give you
SELECT * FROM TBL_Folio
WHERE ThePrimaryKeyColumn > 0
AND Column2 LIKE 'Value2'
I'm not going to list all the permutations, I'm sure you get my point.
I am trying to create a parameterised query for retrieving data back from a table
Essentially I have a table structure of
ID
nvarchar1
ntext
datetime1
datetime2
and I am trying to do a query like so that it selects all the data where the current date is greater than datetime1 and less than datetime2
SELECT
ID, nvarchar1,
ntext,
datetime1,
datetime2
FROM
TABLEName
WHERE
datetime1 >= #CurrentDate
AND datetime2 <= #CurrentDate
I want to make the columns parameters such as
#TableName, #CurrentDate, #StartDate, #EndDate
DECLARE #TableName NVARCHAR(100);
SET #TableName = '[Surveys].[dbo].[Table]'
DECLARE #CurrentDate DateTime;
SET #CurrentDate = GETDATE();
DECLARE #StartDate NVARCHAR(100);
SET #StartDate = 'datetime1'
DECLARE #EndDate NVARCHAR(100);
SET #EndDate = 'datetime2'
DECLARE #sql nvarchar(1000)
SET #sql = 'SELECT * FROM ' + #TableName + 'WHERE' + #EndDate + '>=' + #CurrentDate + 'AND' + #StartDatedatetime1 + '<=' + #CurrentDate
EXEC(#sql)
The data is going to be coming from a SP data source so I have no control of the column names etc. and when I create the SP Lists they automatically assign to a table column of that type so this is why I need to columns to be parameters.
Using the above code which I thought should work returns
Msg 241, Level 16, State 1, Line 14
Conversion failed when converting date and/or time from character string.
What am I doing wrong?
Try the below. As #GordonLinoff stated, you where missing the single quotes (') from around the #CurrentDate variable. Also, you where passing a DATETIME parameter in to the #sql variable which is an NVARCHAR data type. These are not implicitly converted, so the #CurrentDate variable needs to be converted to a NVARCHAR first:
DECLARE #sql nvarchar(1000)
SET #sql = 'SELECT * FROM ' + #TableName
+ ' WHERE ' + #EndDate + ' >= ''' + CONVERT(NVARCHAR(50), #CurrentDate,120)
+ ''' AND ' + #StartDate + ' <= ''' + CONVERT(NVARCHAR(50), #CurrentDate,120) + ''''
EXEC(#sql)
You are pretty close. I would construct the SQL for the table and then use parameters for the current date: This would be something like this:
SET #sql = '
SELECT *
FROM #TableName
WHERE datetime2 >= #CurrentDate AND datetime1 <= #CurrentDate';
SET #sql = REPLACE(#sql, '#TableName', #TableName);
exec sp_executesql #sql, N'#CurrentDate date', #CurrentDate = #CurrentDate;
Incidentally, the problem with your query is the lack of single quotes around the date constants.
EDIT:
You cannot substitute column or table names using parameters. I would write the code as:
SET #sql = '
SELECT *
FROM #TableName
WHERE #datetime2 >= #CurrentDate AND #datetime1 <= #CurrentDate';
SET #sql = REPLACE(#sql, '#TableName', #TableName);
SET #sql = REPLACE(#sql, '#datetime1', #DateTime1);
SET #sql = REPLACE(#sql, '#datetime2', '#DateTime2);
. . .
I use REPLACE() for this type of operation because the code is easier to understand and to maintain.
I have something strange:
Declare #SQLQuery As nvarchar(Max)
Declare #maxdat date
Declare #dateColumn nvarchar(10)
Set #maxdat = GETDATE()
Set #dateColumn = 'ERDAT'
Set #SQLQuery = 'SELECT * FROM myTable WHERE #dateColumn <= #maxdat'
Execute sp_executesql #SQLQuery, N'#maxdat date, #dateColumn nvarchar(10)', #maxdat, #dateColumn
This will fail with Conversion failed when converting date and/or time from character string.
But the following will work just fine:
Set #SQLQuery = 'SELECT * FROM myTable WHERE ERDAT <= #maxdat'
Try the updated code like this. Just put the #dateColumn parameter outside the query string
Declare #SQLQuery As nvarchar(Max)
Declare #maxdat date
Declare #dateColumn nvarchar(10)
Set #maxdat = GETDATE()
Set #dateColumn = 'ERDAT'
Set #SQLQuery = 'SELECT * FROM myTable WHERE ' + #dateColumn + ' <= #maxdat'
Execute sp_executesql #SQLQuery, N'#maxdat date', #maxdat
Problem is with your dynamic SQL. the way you are doing it the variables will go as strings. You have to use the "+" symbol and do it like example below so that SQL can parse it and use 'variable value' instead of 'variable name'.
SELECT GETDATE() AS A
INTO
#Temp
Declare #SQLQuery As nvarchar(Max)
Declare #maxdat date
Declare #dateColumn nvarchar(10)
Set #maxdat = DATEADD(Day,1,GETDATE())
Set #dateColumn = 'A'
--Set #SQLQuery = 'SELECT * FROM #Temp WHERE #dateColumn >= #maxdat'
Set #SQLQuery = 'SELECT * FROM #Temp WHERE ' + #dateColumn + ' >= '+ CAST(#maxdat AS nvarchar(10))
--Print #SQLQuery
Execute sp_executesql #SQLQuery, N'#maxdat date, #dateColumn nvarchar(10)', #maxdat, #dateColumn
I wrote simply procedure, that should return some data.
When I simply replace execute query and put SELECT 1 It is returning 1. But when i paste EXECUTE query, it is return nothing. What should I do?
ALTER PROCEDURE [dbo].[PTC_Repor]
#camp VARCHAR(50),
#StartTime DATETIME,
#EndTime DATETIME
AS
BEGIN
EXECUTE('
SELECT ID,NAME
FROM [hpsdb].[dbo]. [' + #camp + ']
WHERE IS_CLEAR = 0
AND SUCCESS_COUNT = 0
AND DATA_STATUS = 5
AND CALL_TIME > ''' + #StartTime + '''
AND CALL_TIME <= ''' + #EndTime + '''
')
END
GO
You could try using sp_executesql so that you can pass the datetime values across directly and not force them to become strings:
declare #Parms nvarchar(max)
set #Parms = '#StartTime datetime, #EndTime datetime'
declare #SQL nvarchar(max)
set #SQL = 'SELECT ID,NAME
FROM [hpsdb].[dbo]. [' + #camp + ']
WHERE IS_CLEAR = 0
AND SUCCESS_COUNT = 0
AND DATA_STATUS = 5
AND CALL_TIME > #StartTime
AND CALL_TIME <= #EndTime'
EXEC sp_executesql #SQL,#Prams,#StartTime,#EndTime
The following stored procedure works correctly execpt when I pass in the #NameSubstring parameter. I know I am not dynamically building the like clause properly. How can I build the like clause when this parameter also needs to be passed as a parameter in the EXEC sp_executesql call near the bottom of the procedure?
ALTER PROCEDURE [dbo].[spGetAutoCompleteList]
(
#AutoCompleteID int,
#StatusFlag int,
#NameSubstring varchar(100),
#CompanyID int,
#ReturnMappings bit,
#ReturnData bit
)
AS
DECLARE #ErrorCode int,
#GetMappings nvarchar(500),
#Debug bit,
#Select AS NVARCHAR(4000),
#From AS NVARCHAR(4000),
#Where AS NVARCHAR(4000),
#Sql AS NVARCHAR(4000),
#Parms AS NVARCHAR(4000)
SET #ErrorCode = 0
SET #Debug = 1
BEGIN TRAN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
IF #AutoCompleteID IS NOT NULL OR #StatusFlag IS NOT NULL OR #NameSubstring IS NOT NULL
BEGIN
SET #Select = '
SELECT ac.AutoCompleteID,
ac.AutoCompleteName,
ac.CompanyID,
ac.StatusFlag,
ac.OwnerOperID,
ac.CreateDT,
ac.CreateOperID,
ac.UpdateDT,
ac.UpdateOperID,
ac.SubmitOperID,
ac.SubmitDT,
ac.ReviewComments'
SET #GetMappings = '
Select ac.AutoCompleteID'
IF #ReturnData = 1
BEGIN
SET #Select = #Select + '
, ac.AutoCompleteData'
END
SET #From = '
FROM tbAutoComplete ac'
SET #Where = '
WHERE 1=1'
IF #AutoCompleteID IS NOT NULL
BEGIN
SET #Where = #Where + '
AND ac.AutoCompleteID = CAST(#AutoCompleteID AS nvarchar)'
END
IF #StatusFlag IS NOT NULL
BEGIN
SET #Where = #Where + '
AND ac.StatusFlag = CAST(#StatusFlag AS nvarchar)'
END
IF #NameSubstring IS NOT NULL
BEGIN
SET #Where = #Where + '
AND ac.AutoCompleteName like #NameSubstring' + '%'
END
SET #Where = #Where + '
AND ac.CompanyID = + CAST(#CompanyID AS nvarchar)'
SET #Sql = #Select + #From + #Where
SET #Parms = '
#AutoCompleteID int,
#StatusFlag int,
#NameSubstring varchar(100),
#CompanyID int'
EXEC sp_executesql #Sql,
#Parms,
#AutoCompleteID,
#StatusFlag,
#NameSubstring,
#CompanyID
IF #ReturnMappings = 1
BEGIN
SET #GetMappings = 'Select * FROM tbAutoCompleteMap acm WHERE acm.AutoCompleteID IN(' + #GetMappings + #From + #Where + ')'
--EXEC sp_executesql #GetMappings
END
IF #Debug = 1
BEGIN
PRINT #GetMappings
PRINT #Sql
END
END
SELECT #ErrorCode = #ErrorCode + ##ERROR
IF #ErrorCode <> 0
BEGIN
SELECT '<FaultClass>1</FaultClass><FaultCode>1</FaultCode>'
+ '<FaultDesc>Internal Database Error.</FaultDesc>'
+ '<FaultDebugInfo>(spGetAutoCompleteList): There was an error while trying to SELECT from tbAutoComplete.</FaultDebugInfo>'
ROLLBACK TRAN
RETURN
END
COMMIT TRAN
#NameString needs to be outside of the quotes. To get #NameString% enclosed in quotes, you use two single quotes to escape the quote character as a literal.
SET #Where = #Where + '
AND ac.AutoCompleteName like ''' + #NameSubstring + '%'''
To avoid SQL injection, do not use concatenation when adding the parameter to your SQL statement. I strongly recommend that you use this format:
IF #NameSubstring IS NOT NULL BEGIN
SET #Where += 'AND ac.AutoCompleteName LIKE #NameSubstring + char(37)'
END
By using char(37) instead of '%' you avoid having to escape the apostrophes around the string literal
If you wanted to put a wildcard at either side, then you would use
IF #NameSubstring IS NOT NULL BEGIN
SET #Where += 'AND ac.AutoCompleteName LIKE char(37) + #NameSubstring + char(37)'
END
-----------------------------------------------------------------------------
In case someone believes I am wrong, here's proof that concatenation is a risk.
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestInjection]') AND type in (N'U')) BEGIN
create table TestInjection(ID int, Value nvarchar(10))
insert into TestInjection (ID,Value)
Values
(1,'Tom'),
(2,'Fred'),
(3,'Betty'),
(4,'Betty2'),
(5,'Betty3'),
(6,'George')
END
declare #NameSubstring nvarchar(1000) = 'Bet'
--declare #NameSubstring nvarchar(1000) = 'Bet%'';delete from TestInjection;select * from TestInjection where value = ''x'
declare #ID int = 2
Declare #sql nvarchar(1000) = 'select * from TestInjection where ID > #ID '
SET #sql +=' AND [Value] like ''' + #NameSubstring + '%'''
Declare #params nvarchar(100) = '#ID int'
exec sp_executesql #sql, #params, #ID
select * from TestInjection
Run it the first time and you will get a resultset with 3 records, and another with all 6 records.
Now swap the declaration of #NameSubstring to the alternative, and re-run. All data in the table has been deleted.
If on the other hand you write your code like:
declare #NameSubstring nvarchar(1000) = 'Bet'
--declare #NameSubstring nvarchar(1000) = 'Bet%'';delete from TestInjection;select * from TestInjection where value = ''x'
declare #ID int = 2
Declare #sql nvarchar(1000) = 'select * from TestInjection where ID > #ID '
SET #sql +=' AND [Value] LIKE #NameSubstring + char(37)'
Declare #params nvarchar(100) = '#ID int, #NameSubstring nvarchar(1000)'
exec sp_executesql #sql, #params, #ID, #NameSubstring
select * from TestInjection
Then you still get the 3 records returned the first time, but you don't lose your data when you change the declaration.
SET #Where = #Where + 'AND ac.AutoCompleteName like ''%' + #NameSubstring + '%'''
So, you are asking how to specify parameters when you use dynamic queries and sp_executesql ?
It can be done, like this:
DECLARE /* ... */
SET #SQLString = N'SELECT #LastlnameOUT = max(lname) FROM pubs.dbo.employee WHERE job_lvl = #level'
SET #ParmDefinition = N'#level tinyint, #LastlnameOUT varchar(30) OUTPUT'
SET #IntVariable = 35
EXECUTE sp_executesql #SQLString, #ParmDefinition, #level = #IntVariable, #LastlnameOUT=#Lastlname OUTPUT
You can read more about it here: http://support.microsoft.com/kb/262499
Perhaps this wouldn't be an issue if you weren't using dynamic SQL. It looks to me like a vanilla query would work just as well and be much more straightforward to read and debug. Consider the following:
SELECT ac.AutoCompleteID,
ac.AutoCompleteName,
ac.CompanyID,
ac.StatusFlag,
ac.OwnerOperID,
ac.CreateDT,
ac.CreateOperID,
ac.UpdateDT,
ac.UpdateOperID,
ac.SubmitOperID,
ac.SubmitDT,
ac.ReviewComments
FROM tbAutoComplete ac
WHERE ((ac.AutoCompleteID = CAST(#AutoCompleteID AS nvarchar) OR (#AutoCompleteID IS NULL))
AND ((ac.StatusFlag = CAST(#StatusFlag AS nvarchar)) OR (#StatusFlag IS NULL))
AND ((ac.AutoCompleteName like #NameSubstring + '%') OR (#NameSubstring IS NULL))
AND ((ac.CompanyID = CAST(#CompanyID AS nvarchar)) OR (#CompanyID IS NULL))
This is much simpler, clearer etc. Good luck!