Amount of single quotes when running dynamic SQL with variable - sql-server

I am having a real hard time sorting out the number of ' I should have within the following SQL statement:
declare #sql varchar(max)
declare #LetterID varchar(max) = 'c01as1'
set #sql =
'SELECT fltr.tency_seq_no FROM OPENQUERY(loopback,
''SET FMTONLY OFF; EXEC BST.[LET].[LETTERBUILD] #LetterCode =
''''\\SVR-QL4APPLIVE\QLSHAREPOINT\LETTERS\DATAFILES\
'''+#LetterID+'''
.csv''''
WITH RESULT SETS (tency_seq_no VARCHAR(255))''
) AS fltr'
exec (#sql)
Current error:
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near 'c01as1'
Just to clarify, the error is referring to the #LetterID variable within the dynamic SQL , not when declaring the parameter
Print of # SQL
SELECT fltr.tency_seq_no
FROM OPENQUERY(loopback, 'SET FMTONLY OFF; EXEC BST.[LET].[LETTERBUILD] #LetterCode = ''\\SVR-QL4APPLIVE\QLSHAREPOINT\LETTERS\DATAFILES\'c01as1'.csv''
WITH RESULT SETS (tency_seq_no VARCHAR(255));'
) AS fltr
Any help would be appreciated!

I think the error has got something to do with unwanted line breaks etc in the original code. Try this instead:
DECLARE #sql VARCHAR(MAX);
DECLARE #LetterID VARCHAR(MAX) = 'c01as1';
SET #sql = 'SELECT fltr.tency_seq_no FROM OPENQUERY(loopback,
''SET FMTONLY OFF; EXEC BST.[LET].[LETTERBUILD] #LetterCode = ''''\\SVR-QL4APPLIVE\QLSHAREPOINT\LETTERS\DATAFILES\'
+ #LetterID + '.csv''''
WITH RESULT SETS (tency_seq_no VARCHAR(255))''
) AS fltr';
EXEC (#sql);

When I ran into this difficulty a few months ago, I created a function that would perform a double-up on the quotes for dynamic SQL. Instead of searching the string manually each time for quotes needing to be doubled, this scalar function can perform this task. This can prevent potentially cluttering up the script when future modifications are performed, such as adding additional variables, as well as improving readability.
Function as follows:
CREATE FUNCTION dbo.fn_duplicateQuotes
(#string varchar(max),
#level int)
RETURNS varchar(max)
AS
BEGIN
/*Doubles-up quotation marks for nested dynamic SQL
level can be set greater than 1 to add additional doubled-up quotes
for further nested dynamic SQL*/
/*Double up quotes*/
set #string = REPLACE(#string, '''', REPLICATE('''', (#level) * 2))
/*Return Value*/
return #string
END
Dynamic SQL as follows:
declare #SQL nvarchar(max)
declare #LetterID varchar(max) = 'c01as1'
set #SQL = 'SET FMTONLY OFF; EXEC BST.[LET].[LETTERBUILD] #LetterCode =
''\\SVR-QL4APPLIVE\QLSHAREPOINT\LETTERS\DATAFILES\' + #LetterID + '.csv''
WITH RESULT SETS (tency_seq_no VARCHAR(255));'
set #SQL = 'SELECT fltr.tency_seq_no FROM OPENQUERY(loopback,
''' + dbo.fn_duplicateQuotes(#SQL, 1) + '''
) AS fltr'
print #SQL
exec (#SQL)
Print of #SQL returns:
SELECT fltr.tency_seq_no FROM OPENQUERY(loopback,
'SET FMTONLY OFF; EXEC BST.[LET].[LETTERBUILD] #LetterCode =
''\\SVR-QL4APPLIVE\QLSHAREPOINT\LETTERS\DATAFILES\c01as1.csv''
WITH RESULT SETS (tency_seq_no VARCHAR(255));'
) AS fltr

Related

Set exec sp_executesql variable

Not SET variable #Set. This return null or SQL return error.
declare #Field as varchar(10);
declare #Set as varchar(10);
set #Field = 'id_fonte'
declare #sql nvarchar(max)
set #sql = 'select ' + #Set + ' = id_fonte
from fontes
where [' + replace(#Field, '''', '''''') + '] = 54';
exec sp_executesql #sql
You have multiple issues here. First, the variable #set has not been given a value, therefore it is NULL. Anytime you use the + operator to do string concatenation, any string + NULL will equal NULL. Thus your #sql variable will be set to NULL. You can see this by changing one your lines as follows:
declare #Set as varchar(10) = '';
Nevertheless, the syntax of the statement stored in the #sql command looks suspect. Add the line
PRINT #sql
before the execution during testing to see the exact syntax of the command that you are executing.
You can use an OUTPUT parameter with sp_executesql, like this:
Note correct use of QUOTENAME to escape the column name.
declare #Field as varchar(10) = 'id_fonte';
declare #Set as varchar(10);
declare #sql nvarchar(max) = '
select #Set = id_fonte
from fontes
where ' + QUOTENAME(#Field) + ' = 54;
';
print #sql; -- for testing
exec sp_executesql
#sql,
N'#Set varchar(10) OUTPUT',
#Set = #Set OUTPUT;

SQL Server 2012: dynamic SQL limitation ( > 4000 chars) (split)

I have this dynamic SQL in a stored procedure:
Declare #template nvarchar(max)
Declare #publishdetails nvarchar(max)
set #publishdetails= ',B.[PublishedBy]=suser_name(),
B.[PublishedDate]=GETDATE() '
set #template='if NOT EXISTS(select * from ' +#DestinationDB+ '.[CLs] where id='+ str(#slid)+')
insert into ' +#DestinationDB+ '.CLs (id,slid,slversion) VALUES ( '+ str(#id)+','+ str(#slid)+','+str(#slversion)+')
update B set
B.[Clientid]=A.clientid,
--.........
B.[CreatedDate] = A.CreatedDate,
B.[ModifiedDate] = A.ModifiedDate,
B.[CreatedBy] = A.CreatedBy,
B.[ModifiedBy] = A.ModifiedBy '+#publishdetails+ --Added publishdetails
'FROM ' + #SourceDB + '.[CLs] as A, '+ #DestinationDB+ '.[CLs] as B
where A.slversion = '+ str(#slversion)+' and A.id='+str(#slid) + 'B.slversion = '+ str(#slversion)+' and B.id='+str(#slid)
print 'template is: ' + #template
exec sp_Executesql #template
When exec sp_Executesql #template is executing, it fails. Because #template is > 4000 chars and is truncated. How can I split it in chunks and execute it the correct way?
You don't need to split the text into parts. You do need to make sure that truncation doesn't occur whilst you're concatenating strings:
If the result of the concatenation of strings exceeds the limit of 8,000 bytes, the result is truncated. However, if at least one of the strings concatenated is a large value type, truncation does not occur.
So, make sure that the first concatenation is working with a large value type (and thus produces a large value type as its result) and every subsequent concatenation should be saved from truncation:
set #template=CONVERT(nvarchar(max),'if NOT EXISTS(select * from ' ) + #DestinationDB + ...
(In this way, you don't have to insert conversions everywhere)
This generates an error:
declare #t nvarchar(max)
set #t = 'select LEN(''' + REPLICATE('A',3000) + REPLICATE('B',3000) + REPLICATE('C',3000) + ''')'
exec sp_executesql #t
And this produces the result 9000:
declare #t nvarchar(max)
set #t = CONVERT(nvarchar(max),'select LEN(''') + REPLICATE('A',3000) + REPLICATE('B',3000) + REPLICATE('C',3000) + ''')'
exec sp_executesql #t
I suggest to use this approach:
Declare #template nvarchar(max) = N''
set #template = #template +N'.... -- Or SELECT instead of SET
Update#1
I run this simple query on my test DB:
DECLARE #query nvarchar(max) = N'',
#i int = 1
WHILE 1000 > #i
BEGIN
SET #query = #query + N'SELECT ##version;'
SET #i = #i+1
END
SELECT LEN (#query)
EXEC sp_executesql #query
I got batch with length of 16983 characters. And execution goes well - no truncation. I guess the problem is inside #SourceDB + '.[CLs] and #DestinationDB+ '.[CLs] tables. Somewhere there you got data truncation.
Try to PRINT your query and run it manually.

Using dynamic WHERE clause with sp_executesql

I can not get dynamic where clause working. The query I use:
IF NOT EXISTS ( SELECT *
FROM sys.tables
WHERE name = 'a' )
BEGIN
CREATE TABLE a ( a INT );
END;
DECLARE #whereClause NVARCHAR(MAX) = ' 1=1 ';
DECLARE #sql NVARCHAR(MAX) = 'SELECT * FROM a WHERE #whereClause';
EXEC sp_executesql #sql, N'#whereClause NVARCHAR(MAX)', #whereClause;
DROP TABLE a;
Then additional question would be: is there any possibility to debug query that is executed with sl_executesql?
As as been stated you can't use the parameters on sp_executesql to replace statement objects, only parameter variables.
If you need to build the WHERE clause dynamically, I find it easier to use REPLACE for the Object components of the statement.
DECLARE #whereClause NVARCHAR(MAX) = ' 1=#whereVariable ';
DECLARE #whereVariable INT = 1;
DECLARE #sql NVARCHAR(MAX) = 'SELECT * FROM a WHERE #whereClause';
SELECT #sql = REPLACE(#sql, '#whereClause', #whereClause)
EXEC sp_executesql #sql
,'#whereVariable INT'
,#whereVariable = #whereVariable;
This means that the statement can be built without interlived + and variables. It then means that the input and output parameters are used as normal.

Dynamic Column Name in SQL in Update statement

DECLARE #sql NVARCHAR(max)
DECLARE #ParmDefinition NVARCHAR(500)
SET #sql = 'UPDATE [Table1] SET [Table1].[#columnName] = TEST';
SET #ParmDefinition = N'#columnName NVARCHAR(50)';
EXEC sp_executesql #sql, #ParmDefinition, #columnName = N'MyColumn';
When I run the above query, I get Invalid column name '#columnName'.. Clearly, the column name is not being replaced when the query is run.
In reality, my #sql variable is much larger and I have many columns I wish to update, thus I would like to avoid doing SET SQL = for all enumerations of the column name.
I'd like to declare the sql string once, and invoke the query with different values. e.g.:
EXEC sp_executesql #sql, #ParmDefinition, #columnName = N'MyColumn';
EXEC sp_executesql #sql, #ParmDefinition, #columnName = N'AnotherColumn';
EXEC sp_executesql #sql, #ParmDefinition, #columnName = N'YetAnotherColumn';
-- And so on
Is something like this possible?
Yes, you have to concatenate the variable outside the string. In other words:
SET #sql = 'UPDATE [Table1] SET [Table1].[' + #columnName + '] = t1.Value ' +
EDIT: Another solution we have used is to replace tokens in the base sql to construct a new sql variable for execution.
DECLARE #sql nvarchar(max) = 'SELECT #ColumnName FROM #TableName';
DECLARE #sql2 nvarchar(max) = REPLACE(REPLACE(#sql,'#ColumnName',#ColumnNameVariable),'#TableName',#TableNameVariable)
EXEC (#sql2)
...Some code that changes the values of #ColumnNameVariable and #TableNameVariable...
DECLARE #sql2 nvarchar(max) = REPLACE(REPLACE(#sql,'#ColumnName',#ColumnNameVariable),'#TableName',#TableNameVariable)
EXEC (#sql2)
And you'll notice that the Declaration and Exec of SQL2 are exactly the same lines in both cases. This lends itself to use in a LOOP if that is applicable. (Except that you wouldn't DECLARE #Sql2 in the loop...just populate/re-populate it).
First of all, kudos for trying to parameterize your dsql using sp_executesql. The problem is, you can only parameterize something you could put into a variable in the first place such as in a search predicate or select list.
However it's still possible; just concatenate the column name with your DSQL string, wrapping it with the quotename function
set #sql = 'update table1 set table1.' + quotename(#ColumnName) + ' = ...

Error in Creating stored procedure

Anybody tell me what's wrong with creating this stored procedure.
CREATE PROC ImportData
AS
BEGIN
DECLARE #DatabasePath VARCHAR(MAX)
SET #DatabasePath = 'E:\ABC.xls'
DECLARE #sql nvarchar(MAX)
SET #sql = '
INSERT INTO [dbo].[Table_1]
SELECT *
FROM OPENROWSET(''Microsoft.Jet.OLEDB.4.0'',
''Excel 8.0;Database=' + #DatabasePath + ',
''SELECT * FROM [Sheet1$]'') AS xlsTable'
EXEC sp_executesql #sql
GO
END
ERROR:-
Incorrect syntax near '#sql'.
Msg 102, Level 15, State 1, Line 2
Incorrect syntax near 'END'.
Remove the GO from within the Stored Procedure
Something like
CREATE PROC ImportData
AS
BEGIN
DECLARE #DatabasePath VARCHAR(MAX)
SET #DatabasePath = 'E:\ABC.xls'
DECLARE #sql nvarchar(MAX)
SET #sql = '
INSERT INTO [dbo].[Table_1]
SELECT *
FROM OPENROWSET(''Microsoft.Jet.OLEDB.4.0'',
''Excel 8.0;Database=' + #DatabasePath + ',
''SELECT * FROM [Sheet1$]'') AS xlsTable'
EXEC sp_executesql #sql
END
You cannot have a batch terminator (GO) in the body of a stored procedure.

Resources