I am using dynamic SQL to build out some statements. Here is a truncated example of a stored proc UpdateFOO. When I debug this stored procedure, the problem is the #SQL variable I am declaring always stay empty! It is supposed to fill with the query. I suspect it has something to do with how I am formatting this, but I cant spot if its a bad formatting error.
CREATE PROC [dbo].[UpdateFOO]
#TEST1 uniqueidentifier,
#TEST2 nvarchar(40),
#TEST3 nvarchar(50),
#TEST4 char(1),
#TEST5 nvarchar(20),
#TEST6 nvarchar(40),
#LINKED_SERVER_NAME nvarchar(max),
#DATABASE_NAME nvarchar(max)
AS
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRAN
DECLARE #SQL nvarchar(max)
SELECT #SQL = 'UPDATE [' + #LINKED_SERVER_NAME + '].[' + #DATABASE_NAME + '].[dbo].[SOME_TABLE]
SET [TEST1]=' + '''' + convert(nvarchar(36), #TEST1) + '''' +',
[TEST2]=' + '''' +#TEST2 + '''' +',
[TEST3]=' + '''' + #TEST3 + '''' +',
[TEST4]='+ '''' + #TEST4 + '''' +',
[TEST5]=' + '''' + #TEST5 + '''' +',
[TEST6]=' + '''' + #TEST6 + '''' +
' WHERE [TEST1] =' + '''' + convert(nvarchar(36), TEST1 )+ '''' +
+ 'SELECT [TEST1] FROM
[' + #LINKED_SERVER_NAME + '].[' + #DATABASE_NAME + '].[Rev].[SOME_TABLE]
WHERE [TEST1] =' + '''' + convert(nvarchar(36), TEST1 )+ '''' +''
PRINT LEN(#SQL)
EXEC (#SQL)
COMMIT
TIA Experts!
Here's how you use ISNULL. If the first value is null, it will return the 2nd value.
SELECT #SQL = 'UPDATE [' + #LINKED_SERVER_NAME + '].[' + #DATABASE_NAME + '].[dbo].[SOME_TABLE]
SET [TEST1]=' + '''' + convert(nvarchar(36), ISNULL(#TEST1, '')) + '''' +',
[TEST2]=' + '''' +ISNULL(#TEST2, '') + '''' +',
[TEST3]=' + '''' + ISNULL(#TEST3, '') + '''' +',
...
etc...
You should use parametrized query. Here's an example:
DECLARE #sql nvarchar(max), #paramlist nvarchar(max)
SELECT #sql= 'UPDATE Table
SET Col1 = #Value1,
Col2 = #Value2
WHERE (1 = 1)'
SELECT #paramlist = '#Value1 nvarchar (256), #Value2 nvarchar (256)'
EXEC sp_executesql #sql, #paramlist, #Value1, #Value2
Related
I have this problem. I have this query in which I used to model a stored procedure and I want to get the value in the column clr_bal_amt. I want it to return as a string from the database column.
However it does not return it. The query executes, but it does not get the value in that column.
The stored procedure looks like this
CREATE PROCEDURE FindBranchVaultBalance
#sol_id varchar(50),
#acct_crncy_code varchar(50)
AS
DECLARE #query nvarchar(max) = N'
select foracid, acct_crncy_code, clr_bal_amt
from dummy_table
where bacid = ''1010000001'' and sol_id = ' + QUOTENAME(#sol_id, '''') + N' and
acct_crncy_code = ' + QUOTENAME(#acct_crncy_code, '''') + N' and
del_flg = ''N'' and acct_cls_flg = ''N''
';
DECLARE #sql nvarchar(max) = N'
SELECT *
FROM OPENQUERY (LINKEDSERVER, N' + QUOTENAME(#query, '''') + N' )
';
EXEC sp_executesql #sql;
GO
I need some form of clarification. Why did this not return that value in that clr_bal_amt column?
Edited
The SQL is looking like this now
ALTER PROCEDURE [dbo].[FindBranchVaultBalance]
#sol_id varchar(50),
#acct_crncy_code varchar(50)
AS
DECLARE #query nvarchar(max) = N'
select foracid, acct_crncy_code, clr_bal_amt
from dummy_table
where bacid = ''1010000001'' and sol_id = ' + QUOTENAME(#sol_id, '''') + N' and
acct_crncy_code = ' + QUOTENAME(#acct_crncy_code, '''') + N' and
del_flg = ''N'' and acct_cls_flg = ''N''
';
DECLARE #sql nvarchar(max) = N'
SELECT *
FROM OPENQUERY (LINKEDSERVER, N' + REPLACE(#query, '''','''''') + N' )
';
EXEC sp_executesql #sql;
Edits2
ALTER PROCEDURE [dbo].[FindAFINACLEBranchVaultBalance]
#sol_id varchar(50),
#acct_crncy_code varchar(50)
AS
DECLARE #query nvarchar(max) = N'
select foracid, acct_crncy_code, clr_bal_amt
from dummy_table
where bacid = ''1010000001'' and sol_id = ' + QUOTENAME(#sol_id, '''') + N' and
acct_crncy_code = ' + QUOTENAME(#acct_crncy_code, '''') + N' and
del_flg = ''N'' and acct_cls_flg = ''N''
';
DECLARE #sql nvarchar(max) = N'
SELECT *
FROM OPENQUERY (LINKEDSERVER, N' + '... N''' + REPLACE(#query, '''','''''') + N''' ...'+ N' )
';
EXEC sp_executesql #sql;
Is there something I am missing?
As I mentioned QUOTENAME accepts a sysname as a parameter (a synonym of nvarchar(128) NOT NULL), and your variable #query is defined as an nvarchar(MAX). As such if it's 129 or more characters long it'll be truncated; your value is clearly more than 128 characters.
Just use REPLACE:
N'... N''' + REPLACE(#query, '''','''''') + N''' ...'
As a bit of shameless self promotion, I explain this scenario more in-depth here.
To add emphasis on the OP's attempt to implement this, your attempt is not the same:
N'... N' + REPLACE(#query, '''','''''') + N' ...'
Notice the lack of trailing and leading single quotes ('). The single quotes are needed. REPLACE replaces characters, it doesn't delimit identify them like QUOTENAME does.
I have the below query, which gives the count of distinct values for each column. But I need to add a where clause condition in the query such as column1 = 'abc'. I am using this generic query so that I can use the same query with other tables also by passing the table name.
DECLARE #query VARCHAR(MAX)
SELECT #query =
'SELECT ' + SUBSTRING((SELECT ',' +'COUNT(DISTINCT(' + column_name + ')) AS ' + column_name + ' '
FROM information_schema.columns
WHERE table_name = 'table_name'
FOR XML PAT('')), 2, 100000) + 'FROM table_name'
--PRINT(#query)
EXECUTE(#query)
I tried passing it as below.
WHERE
table_name = 'table_name'
AND column1 = 'abc' -- compilation error, obviously
WHERE
table_name = 'table_name'
AND 'column1' = 'abc' -- not working, because it will compare the string values column1 and abc. and both are not equal
Then I just gave a try with the below query, but this is also not working, it's generating the wrong query when I try to print.
DECLARE #SQL NVARCHAR(MAX), #tname NVARCHAR(100),
#cname NVARCHAR(100), #acc_num NVARCHAR(50), #dp_code NVARCHAR(100)
SET #cname = 'column_name';
SET #acc_num = 'xyz';
SET #tname = 'table_name';
DECLARE #query VARCHAR(MAX);
SET #SQL = ''
;with cols as (
select Table_Schema, Table_Name, Column_Name, Row_Number() over(partition by Table_Schema, Table_Name
order by ORDINAL_POSITION) as RowNum
from INFORMATION_SCHEMA.COLUMNS
)
SELECT #query =
'SELECT ' + SUBSTRING((SELECT ',' +'COUNT(DISTINCT(' + QUOTENAME(column_name, '') + ')) As ' + QUOTENAME(column_name, '')+ ' '
+ ' WHERE ' + 'column_name' + ' = ''' + #acc_num + ''''
FROM cols
WHERE
table_name = #tname
for xml path('')),2,200000) + 'FROM' #tname
--for xml path('');
PRINT #query
--execute (#query)
This should be what you are after. There's a few comments in the SQL you need to pay attention to:
DECLARE #SQL nvarchar(max),
#tname sysname, --note the datatype change
#cname sysname, --note the datatype change
#acc_num nvarchar(50),
#dp_code nvarchar(100); --This is never used in your sample query
SET #cname = 'column_name';
SET #acc_num = 'xyz';
SET #tname = 'table_name';
SET #SQL = N'SELECT ' + NCHAR(13) + NCHAR(10) +
STUFF((SELECT N',' + NCHAR(13) + NCHAR(10) +
N' ' + N'COUNT DISTINCT(' + QUOTENAME(C.Column_Name) + N') AS ' + QUOTENAME(C.Column_Name)
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE C.Table_Name = #tname
ORDER BY C.ORDINAL_POSITION
FOR XML PATH(N'')),1,10,N'') + NCHAR(13) + NCHAR(10) +
N'FROM ' + QUOTENAME(#tname) + NCHAR(13) + NCHAR(10) +
N'WHERE ' + QUOTENAME(#cname) + N' = #Acc_Num;'
PRINT #SQL; --YOur debugging best friend
EXEC sp_executesql #SQL, N'#Acc_Num varchar(100)',#Acc_Num = #acc_num;
This parametrised the query instead, using sp_executesql, and nicely formats it for your PRINT statement.
If you don't understand then please do ask.
If you want a WHERE clause in the dynamic query, then you have to put it after the FROM clause in the dynamic query.
...
for xml path('')),2,200000) + ' FROM '+ #tname
+ ' WHERE ' + 'column_name' + ' = ''' + #acc_num + ''''
I wish to modify strings in several columns (for example all columns containing the 'sound' string), for example replacing ',' by '.'. Further to this post, I understand I have to use dynamic SQL. I created the following procedure:
USE [myDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[RemoveStringInColumn] (#colName varchar(50), #tableName varchar(50), #to_remove varchar(50), #to_add varchar(50))
AS
DECLARE #sql nvarchar(4000)
SET #sql = 'UPDATE ' + #tableName + ' SET ' + #colName + ' = REPLACE(' + #colName + ',' + #to_remove + ','+ #to_add + ');'
PRINT #sql
EXEC sp_executesql #sql
Which is called by:
EXEC dbo.RemoveStringInColumn 'COL_1', 'TABLE_1', ',', '.'
1) The problem is the #sql command does not contain the little hyphen arond the comma and the dot. How can I solve this?
2) In this post they use a SELECT command to fetch all column names. So far, I managed to fetch column names containing 'sound'.
select COLUMN_NAME AS my_cols
from INFORMATION_SCHEMA.COLUMNS
where table_name = 'TABLE_1' AND COLUMN_NAME LIKE '%sound%'
How can I put column names into a list and use a for loop to go through them calling the RemoveStringInColumn procedure?
Thanks
Just double the single quotes around #to_remove and #to_add
DECLARE #sql NVARCHAR(4000)
SET #sql = 'UPDATE ' + Quotename(#tableName) + ' SET ' + Quotename(#colName)
+ ' = REPLACE(' + Quotename(#colName) + ',''' + #to_remove + ''','''
+ #to_add + ''');'
PRINT #sql
EXEC Sp_executesql
#sql
Update : To do the replace for more than one column
DECLARE #sql NVARCHAR(4000),
#col_list VARCHAR(8000)= ''
SET #col_list = (SELECT ',' + Quotename(COLUMN_NAME) + ' = REPLACE('
+ Quotename(COLUMN_NAME) + ',''' + #to_remove
+ ''',''' + #to_add + ''')'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'TABLE_1'
AND COLUMN_NAME LIKE '%sound%'
FOR xml path(''))
SET #col_list = Stuff(#col_list, 1, 1, '')
SELECT #col_list
SET #sql = 'UPDATE ' + Quotename(#tableName) + ' SET '
+ #col_list
PRINT #sql
EXEC Sp_executesql
#sql
I have created a procedure in dynamic SQL which has a select statement and the code looks like:
ALTER PROCEDURE cagroup (
#DataID INT ,
#days INT ,
#GName VARCHAR(50) ,
#T_ID INT ,
#Act BIT ,
#Key VARBINARY(16)
)
AS
BEGIN
DECLARE #SQL NVARCHAR(MAX)
DECLARE #SchemaName SYSNAME
DECLARE #TableName SYSNAME
DECLARE #DatabaseName SYSNAME
DECLARE #BR CHAR(2)
SET #BR = CHAR(13) + CHAR(10)
SELECT #SchemaName = Source_Schema ,
#TableName = Source_Table ,
#DatabaseName = Source_Database
FROM Source
WHERE ID = #DataID
SET #SQL = 'SELECT ' + #GName + ' AS GrName ,' + #BR
+ #T_ID + ' AS To_ID ,' + #BR
+ #DataID + ' AS DataSoID ,' + #BR
+ #Act + ' AS Active ,' + #BR
+ Key + ' AS key' + #BR
+ 'R_ID AS S_R_ID' + #BR
+ 'FROM' + #DatabaseName + '.'
+ #SchemaName + '.'
+ #TableName + ' t' + #BR
+ 'LEFT OUTER JOIN Gro g ON g.GName = '
+ #GName + #BR + 'AND g.Data_ID] =' + #DataID + #BR
+ 't.[I_DATE] > GETDATE() -' + #days + #BR
+ 'g.GName IS NULL
AND ' + #GName + ' IS NOT NULL
AND t.[Act] = 1' + #BR
PRINT (#SQL)
END
When I am executing this procedure with this statement:
Exec dbo.cagroup 1,10,'[Gro]',1,1,NULL
I am getting the following error.
Msg 245, Level 16, State 1, Procedurecagroup, Line 33
Conversion failed when converting the nvarchar value 'SELECT [Gro] AS GName ,
' to data type int.
Where am I doing wrong?
You need to CAST all numbers to nvarchar in the concatenation.
There is no implicit VBA style conversion to string. In SQL Server data type precedence means ints are higher then nvarchar: so the whole string is trying to be CAST to int.
SET #SQL = 'SELECT ' + #GName + ' AS GrName ,' + #BR
+ CAST(#T_ID AS nvarchar(10)) + ' AS To_ID ,' ...
Edit: Will A has a good point: watch for NULLs!
If you have to build this kind of dynamic SQL, it is better to get the column information from the meta-data than to pass it around.
Select * from Information_Schema.Columns Where Table_name=#TableName
The you have to write an ugly cursor to build the SQL. Expect performance problems. I do lots of this during development to write code for me, but I don't dare run it in production.
I have created a procedure in dynamic SQL which has a select statement and the code looks like:
ALTER PROCEDURE cagroup (
#DataID INT ,
#days INT ,
#GName VARCHAR(50) ,
#T_ID INT ,
#Act BIT ,
#Key VARBINARY(16)
)
AS
BEGIN
DECLARE #SQL NVARCHAR(MAX)
DECLARE #SchemaName SYSNAME
DECLARE #TableName SYSNAME
DECLARE #DatabaseName SYSNAME
DECLARE #BR CHAR(2)
SET #BR = CHAR(13) + CHAR(10)
SELECT #SchemaName = Source_Schema ,
#TableName = Source_Table ,
#DatabaseName = Source_Database
FROM Source
WHERE ID = #DataID
SET #SQL = 'SELECT ' + #GName + ' AS GrName ,' + #BR
+ #T_ID + ' AS To_ID ,' + #BR
+ #DataID + ' AS DataSoID ,' + #BR
+ #Act + ' AS Active ,' + #BR
+ Key + ' AS key' + #BR
+ 'R_ID AS S_R_ID' + #BR
+ 'FROM' + #DatabaseName + '.'
+ #SchemaName + '.'
+ #TableName + ' t' + #BR
+ 'LEFT OUTER JOIN Gro g ON g.GName = '
+ #GName + #BR + 'AND g.Data_ID] =' + #DataID + #BR
+ 't.[I_DATE] > GETDATE() -' + #days + #BR
+ 'g.GName IS NULL
AND ' + #GName + ' IS NOT NULL
AND t.[Act] = 1' + #BR
PRINT (#SQL)
END
When I am executing this procedure with this statement:
Exec dbo.cagroup 1,10,'[Gro]',1,1,NULL
I am getting the following error.
Msg 245, Level 16, State 1, Procedurecagroup, Line 33
Conversion failed when converting the nvarchar value 'SELECT [Gro] AS GName ,
' to data type int.
Where am I doing wrong?
You need to CAST all numbers to nvarchar in the concatenation.
There is no implicit VBA style conversion to string. In SQL Server data type precedence means ints are higher then nvarchar: so the whole string is trying to be CAST to int.
SET #SQL = 'SELECT ' + #GName + ' AS GrName ,' + #BR
+ CAST(#T_ID AS nvarchar(10)) + ' AS To_ID ,' ...
Edit: Will A has a good point: watch for NULLs!
If you have to build this kind of dynamic SQL, it is better to get the column information from the meta-data than to pass it around.
Select * from Information_Schema.Columns Where Table_name=#TableName
The you have to write an ugly cursor to build the SQL. Expect performance problems. I do lots of this during development to write code for me, but I don't dare run it in production.