I am trying to write the following stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spInsertAudit]
(#val1 VARCHAR(100),
#val2 VARCHAR(200))
AS
BEGIN TRY
DECLARE #return_value varchar(max)
/************************************/
/* The Query */
/************************************/
EXEC [dbo].[SerializeJSON]
'INSERT INTO tLogin (EMP, LoginInTime)
VALUES (#val1, #val2)'
END TRY
BEGIN CATCH
/************************************/
/* Get Error results back */
/************************************/
EXEC [dbo].[SerializeJSON]
'SELECT ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage'
END CATCH
The SerializeJSON stored procedure (found here) is this:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SerializeJSON]
(#ParameterSQL AS VARCHAR(MAX))
AS
BEGIN
DECLARE #SQL NVARCHAR(MAX)
DECLARE #XMLString VARCHAR(MAX)
DECLARE #XML XML
DECLARE #Paramlist NVARCHAR(1000)
SET #Paramlist = N'#XML XML OUTPUT'
SET #SQL = 'WITH PrepareTable (XMLString)'
SET #SQL = #SQL + 'AS('
SET #SQL = #SQL + #ParameterSQL + 'FOR XML RAW,TYPE,ELEMENTS'
SET #SQL = #SQL + ')'
SET #SQL = #SQL + 'SELECT #XML=[XMLString]FROM[PrepareTable]'
EXEC sp_executesql #SQL
, #Paramlist
, #XML = #XML OUTPUT
SET #XMLString = CAST(#XML AS VARCHAR(MAX))
DECLARE #JSON VARCHAR(MAX)
DECLARE #Row VARCHAR(MAX)
DECLARE #RowStart INT
DECLARE #RowEnd INT
DECLARE #FieldStart INT
DECLARE #FieldEnd INT
DECLARE #KEY VARCHAR(MAX)
DECLARE #Value VARCHAR(MAX)
DECLARE #StartRoot VARCHAR(100);
SET #StartRoot = '<row>'
DECLARE #EndRoot VARCHAR(100);
SET #EndRoot = '</row>'
DECLARE #StartField VARCHAR(100);
SET #StartField = '<'
DECLARE #EndField VARCHAR(100);
SET #EndField = '>'
SET #RowStart = CharIndex(#StartRoot, #XMLString, 0)
SET #JSON = ''
WHILE #RowStart > 0
BEGIN
SET #RowStart = #RowStart + Len(#StartRoot)
SET #RowEnd = CharIndex(#EndRoot, #XMLString, #RowStart)
SET #Row = SubString(#XMLString, #RowStart, #RowEnd - #RowStart)
SET #JSON = #JSON + '{'
-- for each row
SET #FieldStart = CharIndex(#StartField, #Row, 0)
WHILE #FieldStart > 0
BEGIN
-- parse node key
SET #FieldStart = #FieldStart + Len(#StartField)
SET #FieldEnd = CharIndex(#EndField, #Row, #FieldStart)
SET #KEY = SubString(#Row, #FieldStart, #FieldEnd - #FieldStart)
SET #JSON = #JSON + '"' + #KEY + '":'
-- parse node value
SET #FieldStart = #FieldEnd + 1
SET #FieldEnd = CharIndex('</', #Row, #FieldStart)
SET #Value = SubString(#Row, #FieldStart, #FieldEnd - #FieldStart)
SET #JSON = #JSON + '"' + #Value + '",'
SET #FieldStart = #FieldStart + Len(#StartField)
SET #FieldEnd = CharIndex(#EndField, #Row, #FieldStart)
SET #FieldStart = CharIndex(#StartField, #Row, #FieldEnd)
END
IF LEN(#JSON) > 0
SET #JSON = SubString(#JSON, 0, LEN(#JSON))
SET #JSON = #JSON + '},'
--/ for each row
SET #RowStart = CharIndex(#StartRoot, #XMLString, #RowEnd)
END
IF LEN(#JSON) > 0
SET #JSON = SubString(#JSON, 0, LEN(#JSON))
SET #JSON = '[' + #JSON + ']'
SELECT #JSON
END
When I execute the spInsertAudit stored procedure, I get this error:
Msg 102, Level 15, State 1, Line 8
Incorrect syntax near 'XML'.
So it did write to that table but for some reason it's not returning the JSON and having that error above....
Related
I want to set the value of #Count by executing #Counter within a Begin Try of a stored procedure.
SET #Counter ='SET #Count = (SELECT COUNT(' + #COLUMN + ') FROM ' + #TABLE + ' WHERE CONVERT(VARCHAR(MAX),' + #COLUMN + ') = ''' + #DATATOFIND + ''')'
I have tested the above code and it does give me the expected result for populating the #Count variable inside of a normal sql statement outside of a stored procedure.
Once the #Count variable is populated I want to use it in a print statement.
PRINT '-- No. of Entries in the ' + #TABLE + ' Table = ' + #Count
I have tried to the following two options to get the #Count populated but neither has worked
EXEC #Counter
and
EXECUTE sp_executesql (#Counter)
UPDATE:
After some more research I tried this:
DECLARE #Counter NVARCHAR(1000)
SET #Counter = N'DECLARE #Count NVARCHAR(100); SET #COUNT = (SELECT COUNT(UserId) FROM UserGrp WHERE CONVERT(VARCHAR(MAX),UserId) = ''za02'')'
EXECUTE sp_executesql #Counter
Print #Count
But I receive this error:
Must declare the scalar variable "#Count"
UPDATE: Workaround / Solution to my situation
DECLARE #Counter NVARCHAR(2000)
SET #Counter = 'DECLARE #Count NVARCHAR(100); SET #COUNT = (SELECT COUNT(UserId) FROM UserGrp WHERE CONVERT(VARCHAR(MAX),UserId) = 'to01'); Print '/* No. of Entries in the UserGrp Table - ' + #Count + ' */''
EXEC (#Counter)
This gives me clear information in my result to decide what to do with the created code from the rest of the stored proc
Dynamic SQL requires careful handling:
DECLARE #Counter NVARCHAR(1000);
DECLARE #COUNT BIGINT;
DECLARE #DATATOFIND VARCHAR(100) = 'za02';
DECLARE #TABLE SYSNAME = N'UserGrp';
DECLARE #COLUMN SYSNAME = N'UserId';
SET #Counter = N'SELECT #COUNT = COUNT(<column_name>)
FROM <table_name>
WHERE CONVERT(VARCHAR(MAX),<column_name>) = #DATATOFIND;';
SET #Counter = REPLACE(#Counter, '<column_name>', QUOTENAME(#COLUMN));
SET #Counter = REPLACE(#Counter, '<table_name>', QUOTENAME(#TABLE));
PRINT #Counter; -- debug
EXECUTE sp_executesql #Counter,
N'#DATATOFIND VARCHAR(100), #COUNT BIGINT OUTPUT',
#DATATOFIND,
#COUNT OUTPUT;
SELECT #COUNT;
db<>fiddle demo
Minimum:
params are parameters, not concatenated string
identifiers(here column/table name) - should be quoted for instance using QUOTENAME function
it is good to print query to see if it doing what is expected
parameters set inside dynamic query could be passed to outer block by defining them as OUTPUT
I would like to add the following stored procedure to all existing databases which contain the table schichten. All my approaches have failed so I'm looking for help here.
This is my approach:
IF object_id('tempdb.dbo.#database') is not null
drop TABLE #database
GO
CREATE TABLE #database(id INT identity primary key, name sysname)
GO
SET NOCOUNT ON
INSERT INTO #database(name)
SELECT name
FROM sys.databases
WHERE source_database_id is null
ORDER BY name
SELECT * FROM #database
DECLARE #id INT, #cnt INT, #sql NVARCHAR(MAX), #currentDb SYSNAME;
SELECT #id = 1, #cnt = max(id) FROM #database
WHILE #id <= #cnt
BEGIN
BEGIN TRY
SELECT #currentDb = name
FROM #database
WHERE id = #id
IF OBJECT_ID(#currentDb+'.dbo.schichten') IS NOT NULL
CREATE PROCEDURE #currentDb.[dbo].[Ausw_Tabelle_Taxi_Pers_Jahr]
#ColumnName nvarchar(MAX),
#Selector nvarchar(MAX),
#Gesamtergebnis nvarchar(MAX)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql1 AS NVARCHAR(MAX),
#ASSelector nvarchar(MAX),
#IFPers nvarchar(MAX);
IF #Selector = 'konz'
BEGIN
SET #ASSelector = 'Taxi'
SET #IFPers=''
END
ELSE
BEGIN
SET #ASSelector = 'Personal'
SET #IFPers = '[name] AS Name,'
END
SET #sql1 = N';WITH temp AS (SELECT *
FROM (
SELECT
ISNULL((DATENAME(m,[datum])+ cast(datepart(yyyy,[datum]) as varchar(5))),0) AS MONTHYEAR,
ISNULL(['+ #Selector +'],0) AS '+ #ASSelector +','+ #IFPers +'
ISNULL((ISNULL([umsum],0) +
ISNULL([sonst_0],0) +
ISNULL([sonst_7],0) +
ISNULL([sonst_16],0) +
ISNULL([sonst_z],0) -
ISNULL([ff],0)),0) AS UMSATZSUMME
FROM [dbo].[schichten]
) AS SOURCE
PIVOT (SUM([UMSATZSUMME]) FOR [MONTHYEAR] IN ('+ #ColumnName + N' )) AS UMSAETZE )
SELECT *, '+ #Gesamtergebnis +' AS Gesamtergebnis FROM temp ORDER BY '+ #ASSelector +''
EXEC sp_executesql #sql
END
END TRY
BEGIN CATCH
END CATCH
SET #id = #id + 1;
END
GO
I am hoping that there is someone who can help me.
You have to execute the create procedure separately, so if you wrap it up into a variable and use exec sp_executesql it should work.
IF object_id('tempdb.dbo.#database') is not null
drop TABLE #database
GO
CREATE TABLE #database(id INT identity primary key, name sysname)
GO
SET NOCOUNT ON
INSERT INTO #database(name)
SELECT name
FROM sys.databases
WHERE source_database_id is null
ORDER BY name
SELECT * FROM #database
DECLARE #id INT, #cnt INT, #sql NVARCHAR(MAX), #currentDb SYSNAME;
SELECT #id = 1, #cnt = max(id) FROM #database
WHILE #id <= #cnt
BEGIN
BEGIN TRY
SELECT #currentDb = name
FROM #database
WHERE id = #id
IF OBJECT_ID(#currentDb+'.dbo.schichten') IS NOT NULL
begin
set #sql = 'CREATE PROCEDURE '+#currentDb+'.[dbo].[Ausw_Tabelle_Taxi_Pers_Jahr]
#ColumnName nvarchar(MAX),
#Selector nvarchar(MAX),
#Gesamtergebnis nvarchar(MAX)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql1 AS NVARCHAR(MAX),
#ASSelector nvarchar(MAX),
#IFPers nvarchar(MAX);
IF #Selector = ''konz''
BEGIN
SET #ASSelector = ''Taxi''
SET #IFPers=''''
END
ELSE
BEGIN
SET #ASSelector = ''Personal''
SET #IFPers = ''[name] AS Name,''
END
SET #sql1 = N'';WITH temp AS (SELECT *
FROM (
SELECT
ISNULL((DATENAME(m,[datum])+ cast(datepart(yyyy,[datum]) as varchar(5))),0) AS MONTHYEAR,
ISNULL([''+ #Selector +''],0) AS ''+ #ASSelector +'',''+ #IFPers +''
ISNULL((ISNULL([umsum],0) +
ISNULL([sonst_0],0) +
ISNULL([sonst_7],0) +
ISNULL([sonst_16],0) +
ISNULL([sonst_z],0) -
ISNULL([ff],0)),0) AS UMSATZSUMME
FROM [dbo].[schichten]
) AS SOURCE
PIVOT (SUM([UMSATZSUMME]) FOR [MONTHYEAR] IN (''+ #ColumnName + N'' )) AS UMSAETZE )
SELECT *, ''+ #Gesamtergebnis +'' AS Gesamtergebnis FROM temp ORDER BY ''+ #ASSelector +''''
EXEC sp_executesql #sql1
END'
EXEC sp_executesql #sql
END TRY
BEGIN CATCH
END CATCH
SET #id = #id + 1;
END
GO
Assuming this is a one time need as opposed to a nightly maintenance task, you can use a built-in stored procedure, sys.sp_MSforeachdb, to execute a statement in each database. It is safe to use and has been discussed extensively on the web. However, it is an undocumented feature and can be removed without notice by Microsoft so you don't want to depend on it for recurring tasks.
Create and validate your statement in one database, then use this stored procedure to execute it in all of the databases. ? is the placeholder for the database name.
EXEC sys.sp_MSforeachdb #command1 =
'IF OBJECT_ID(''?.dbo.schichten'') IS NOT NULL
AND OBJECT_id(''?.[dbo].[Ausw_Tabelle_Taxi_Pers_Jahr]'') IS NOT NULL
BEGIN
CREATE PROCEDURE ?.[dbo].[Ausw_Tabelle_Taxi_Pers_Jahr]
#ColumnName nvarchar(MAX),
#Selector nvarchar(MAX),
#Gesamtergebnis nvarchar(MAX)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql1 AS NVARCHAR(MAX),
#ASSelector nvarchar(MAX),
#IFPers nvarchar(MAX);
IF #Selector = ''konz''
BEGIN
SET #ASSelector = ''Taxi''
SET #IFPers=''''
END
ELSE
BEGIN
SET #ASSelector = ''Personal''
SET #IFPers = ''[name] AS Name,''
END
SET #sql1 = N'';WITH temp AS (SELECT *
FROM (
SELECT
ISNULL((DATENAME(m,[datum])+ cast(datepart(yyyy,[datum]) as varchar(5))),0) AS MONTHYEAR,
ISNULL([''+ #Selector +''],0) AS ''+ #ASSelector +'',''+ #IFPers +''
ISNULL((ISNULL([umsum],0) +
ISNULL([sonst_0],0) +
ISNULL([sonst_7],0) +
ISNULL([sonst_16],0) +
ISNULL([sonst_z],0) -
ISNULL([ff],0)),0) AS UMSATZSUMME
FROM [dbo].[schichten]
) AS SOURCE
PIVOT (SUM([UMSATZSUMME]) FOR [MONTHYEAR] IN (''+ #ColumnName + N'' )) AS UMSAETZE )
SELECT *, ''+ #Gesamtergebnis +'' AS Gesamtergebnis FROM temp ORDER BY ''+ #ASSelector +''''
EXEC sp_executesql #sql1
END
'
I have a table #months as below
MONTH_ID FISCAL_YEAR_MONTH MIN_DATE MAX_DATE
1 FSA201510 20151001 20151031
2 FSA201511 20151101 20151130
3 FSA201512 20151201 20151204
I am using below dynamic query to update a column SCCount in table mastercount(has 3 rows)-
DECLARE #sql VARCHAR(8000), #fym varchar(6)
DECLARE #I INT, #ROWS INT
SET #ROWS=(SELECT count(*) from #MONTHS)
SET #I=1
WHILE #I<=#ROWS
BEGIN
SET #fym=(SELECT RIGHT(FISCAL_YEAR_MONTH,6) from #MONTHS where month_id=#I)
SET #sql = 'UPDATE mastercount'
SET #sql += ' SET SCCount = (SELECT count(*) from '
SET #sql += ' dw_extract.dbo.dw_fsa_' + cast(#fym as varchar(6))+ ') WHERE row=#I'
EXEC (#sql)
SET #I=#I+1
END
This gives an error Must declare the scalar variable "#I".
Why is this happening?
You need to change this
SET #sql += ' dw_extract.dbo.dw_fsa_' + cast(#fym as varchar(6))+ ') WHERE row=#I'
to this
SET #sql += ' dw_extract.dbo.dw_fsa_' + cast(#fym as varchar(6))+ ') WHERE row=' + CAST(#I AS NVARCHAR(10))
That's because #I is not recognized by your dynamic query. You should parameterized #I instead and use sp_executesql to run your query:
DECLARE #sql NVARCHAR(8000), #fym VARCHAR(6)
DECLARE #I INT, #ROWS INT
SET #ROWS = (SELECT COUNT(*) FROM #MONTHS)
SET #I = 1
WHILE #I <= #ROWS
BEGIN
SET #fym = (SELECT RIGHT(FISCAL_YEAR_MONTH,6) FROM #MONTHS WHERE month_id = #I)
SET #sql = N'UPDATE mastercount'
SET #sql += N' SET SCCount = (SELECT count(*) from '
SET #sql += N' dw_extract.dbo.dw_fsa_' + cast(#fym as varchar(6))+ N') WHERE row=#I'
EXEC sp_executesql #sql, N'#I INT', #I
SET #I = #I + 1
END
As I understand it, the following code:
declare #csvnmbr nvarchar(3)
declare #sql nvarchar(400)
set #csvnmbr = 1
set #sql = 'BULK INSERT #tablename FROM ''C:\TEMP\'+#csvnmbr+'.csv''
WITH (FIELDTERMINATOR = '','')'
while #csvnmbr < 4
begin
print #sql
set #csvnmbr = #csvnmbr + 1
end
GO
Should print:
BULK INSERT #tablename FROM 'C:\TEMP\1.csv' WITH (FIELDTERMINATOR = ',')
BULK INSERT #tablename FROM 'C:\TEMP\2.csv' WITH (FIELDTERMINATOR = ',')
BULK INSERT #tablename FROM 'C:\TEMP\3.csv' WITH (FIELDTERMINATOR = ',')
but it doesn't, it prints:
BULK INSERT #tablename FROM 'C:\TEMP\1.csv' WITH (FIELDTERMINATOR = ',')
BULK INSERT #tablename FROM 'C:\TEMP\1.csv' WITH (FIELDTERMINATOR = ',')
BULK INSERT #tablename FROM 'C:\TEMP\1.csv' WITH (FIELDTERMINATOR = ',')
Does anyone know why the loop won't print the number sequence but just sticks to the first csv number instead?
If I do this though:
declare #csvnmbr nvarchar(3)
declare #sql nvarchar(400)
set #csvnmbr = 1
set #sql = 'HI'
while #csvnmbr < 4
begin
print #sql
print #csvnmbr
set #csvnmbr = #csvnmbr + 1
end
GO
There is no apparent need to write the #sql inside the loop. Any idea why the fact that the #sql statement is dynamic means it needs to be reset each time?
Because it is set once before loop starts. You need to set #sql inside loop:
DECLARE #csvnmbr INT = 1
,#sql NVARCHAR(MAX);
WHILE #csvnmbr < 4
BEGIN
SET #sql = N'BULK INSERT #tablename FROM ''C:\TEMP\'' +
CAST(#csvnmbr AS NVARCHAR(10)) +
'.csv'' WITH (FIELDTERMINATOR = '','')';
PRINT #sql;
SET #csvnmbr += 1;
END
About your question why it needs to be inside loop:
declare #csvnmbr nvarchar(3)
declare #sql nvarchar(400)
set #csvnmbr = 1
/* At this point #sql if fixed string, during concatenation #csvnmbr was
replaced by its actual value */
set #sql = 'BULK INSERT #tablename FROM ''C:\TEMP\'+#csvnmbr+'.csv''
WITH (FIELDTERMINATOR = '','')'
while #csvnmbr < 4
begin
print #sql /* Here sql will be always the same you need to set it again */
set #csvnmbr = #csvnmbr + 1
end
What you trying to achieve is passing string by reference. You can mimic it like this:
DECLARE #csvnmbr NVARCHAR(3) = '1'
,#sql NVARCHAR(MAX);
SET #sql =
N'BULK INSERT #tablename FROM ''C:\TEMP\#csvnmbr.csv'' WITH (FIELDTERMINATOR = '','')';
WHILE #csvnmbr < 4
BEGIN
/* Warning!!! This will execute code in #sql*/
EXEC [dbo].[sp_executesql]
#sql,
N'#csvnmbr nvarchar(3)',
#cvnmbr; /* Will substitute #csvnmbr with passed value */
SET #csvnmbr += 1;
END
Keep your statement inside the while loop
declare #csvnmbr nvarchar(3)
declare #sql nvarchar(400)
set #csvnmbr = 1
while #csvnmbr < 4
begin
set #sql = 'BULK INSERT #tablename FROM ''C:\TEMP\'+#csvnmbr+'.csv''
WITH (FIELDTERMINATOR = '','')'
print #sql
set #csvnmbr = #csvnmbr + 1
end
GO
the while loop iteration happens between the while loop condition begin and end blocks.
WHILE (Some Condition) --<-- first the condition is checked
BEGIN
--<-- if the while condition is evaluated to true
-- code between the BEGIN and END is executed
-- it keeps getting executing until while condition returns false
END
You need to fill #sql in each iteration:
DECLARE #csvnmbr NVARCHAR(3);
DECLARE #sql NVARCHAR(400);
SET #csvnmbr = 1;
SET #sql = 'BULK INSERT #tablename FROM ''C:\TEMP\' + #csvnmbr + '.csv''
WITH (FIELDTERMINATOR = '','')';
WHILE #csvnmbr < 4
BEGIN
PRINT #sql;
SET #csvnmbr = #csvnmbr + 1;
SET #sql = 'BULK INSERT #tablename FROM ''C:\TEMP\' + #csvnmbr + '.csv''
WITH (FIELDTERMINATOR = '','')';
END;
GO
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!