Table Columns as parameters to Stored Procedure [duplicate] - 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.

Related

Passing special character [char(11), char(7)] in stored procedure as string

I want to search in table for all records with a special characters - char(11), char(7) etc.
I have found one stored procedure which helps me to find it. But it is not accepting the input parameters as follows:
EXEC sp_FindStringInTable '%'+char(7)+'%', 'CPOA-TALENTLink-Test-Leeds', 'TALENT_Contact_ChangeLog'
Error:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near '+'.
Stored procedure:
CREATE PROCEDURE dbo.sp_FindStringInTable
#stringToFind NVARCHAR(100),
#schema SYSNAME,
#table SYSNAME
AS
BEGIN TRY
DECLARE #sqlCommand VARCHAR(MAX) = 'SELECT * FROM [' + #schema + '].[' + #table + '] WHERE '
SELECT #sqlCommand = #sqlCommand + '[' + COLUMN_NAME + '] LIKE ''' + #stringToFind + ''' OR '
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = #schema
AND TABLE_NAME = #table
AND DATA_TYPE IN ('char','nchar','ntext','nvarchar','text','varchar')
SET #sqlCommand = LEFT(#sqlCommand, LEN(#sqlCommand) - 3)
EXEC (#sqlCommand)
PRINT #sqlCommand
END TRY
BEGIN CATCH
PRINT 'There was an error. Check to make sure object exists.'
PRINT error_message()
END CATCH
As mentioned in error, I'm unable to search through table for special characters.
In calls to procedures expressions other than literals or variables don't work.
Assign the concatenation to a variable and pass that variable to the procedure.
DECLARE #p varchar(max) = '%' + char(7) + '%';
EXEC sp_FindStringInTable #p, 'CPOA-TALENTLink-Test-Leeds', 'TALENT_Contact_ChangeLog';
Literal string concatenation like '[' + #schema + ']' and ' LIKE ''' + #stringToFind + '''' isn't safe. Far from it. I suspect that parametrising your query is going to fix this:
CREATE PROCEDURE dbo.sp_FindStringInTable #stringToFind NVARCHAR(100), #schema sysname, #table sysname
AS
BEGIN TRY
DECLARE #sqlCommand varchar(max);
SET #sqlCommand = N'SELECT *' + NCHAR(10) + --Formatting yoru dynamic SQL is a very good idea
N'FROM ' + QUOTENAME(#schema) + N'.' + QUOTENAME(#table) + NCHAR(10) +
N'WHERE' +
STUFF((SELECT NCHAR(10) + N' AND ' + QUOTENAME(COLUMN_NAME) + N'LIKE #String'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = #schema
AND TABLE_NAME = #table
AND DATA_TYPE IN ('char','nchar','ntext','nvarchar','text','varchar')
FOR XML PATH(N'')),1,6,N'')
PRINT #sqlCommand; --your best friend
EXEC sp_executesql #sqlCommand, N'String nvarchar(100)', #String = #stringToFind;
END TRY
BEGIN CATCH
PRINT 'There was an error. Check to make sure object exists.'
PRINT error_message()
END CATCH
Note that I have not tested the above. Your best friend is there to help you debug.

Dynamic SQL, Empty Variable, Formatting Issue?

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

Update Statement in a Stored Procedure

I have a stored Procedure called Active and the code is:
CREATE PROCEDURE dbo.Active
(
#ID INT ,
#Source VARCHAR(25)
)
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 = #ID
SET #SQL = 'UPDATE Source_Table' + #BR
+ 'SET __ACTIVE = CASE WHEN rn = 1 THEN 1 ELSE 0 END' + #BR
+ 'FROM ( ' + #BR + 'SELECT ROW_NUMBER() OVER (PARTITION BY '
+ #Source + ' ORDER BY __REC_ID DESC) AS rn
, * FROM ' + #DatabaseName + '.' + #SchemaName + '.' + #TableName
+ #BR + ') Source_Table' + #BR
EXEC #SQL
END
The problem is I am using thsi procedure in another procedure so Everytime that Procedure runs this procedure also runs and does the update to whole table.
The main reason for that update is to check for the duplicates on the table and set the duplicates to 0 and remaining to 1.
I dont want to run this update for whole table but I want the update to run only for Active duplicates.
Is there a way to do it?
To reiterate your issue. You are calling the above stored procedure from another stored procedure. I assume that the parent procedure is what is determining what you call "Active duplicates". If that is the case, then you have a few options:
1) Temp table, have the first procedure create a global temporary table and use it in nested procedure. Make sure to clean up after.
--Base procedure creates global temp table with proper values;
SELECT ID
INTO ##ActiveDups
FROM DUPTABLE
WHERE SOMECONDITION = SOMECONDITION
--Join global temp table on query
SET #SQL = 'UPDATE Source_Table' + #BR
+ 'SET __ACTIVE = CASE WHEN rn = 1 THEN 1 ELSE 0 END' + #BR
+ 'FROM ( ' + #BR + 'SELECT ROW_NUMBER() OVER (PARTITION BY '
+ #Source + ' ORDER BY __REC_ID DESC) AS rn
, * FROM ' + #DatabaseName + '.' + #SchemaName + '.' + #TableName
+ #BR + ') Source_Table' + #BR
+ ' INNER JOIN ##ActiveDups ad ON ad.ID = Source_Table.ID'
--Drop global temp table
DROP TABLE ##ActiveDups
2) Parameter, pass a comma separated list to the nested procedure and filter with IN or EXISTS clause. Not very scalable. (See added parameter and last line of query)
CREATE PROCEDURE dbo.Active
(
#ID INT ,
#Source VARCHAR(25),
#List VARCHAR(MAX)
)
--...
SET #SQL = 'UPDATE Source_Table' + #BR
+ 'SET __ACTIVE = CASE WHEN rn = 1 THEN 1 ELSE 0 END' + #BR
+ 'FROM ( ' + #BR + 'SELECT ROW_NUMBER() OVER (PARTITION BY '
+ #Source + ' ORDER BY __REC_ID DESC) AS rn
, * FROM ' + #DatabaseName + '.' + #SchemaName + '.' + #TableName
+ #BR + ') Source_Table' + #BR
+ ' WHERE SOMECONDITION IN ' #List
3) Add logic to your dynamic SQL to fetch the proper results. (See last line, which was appended. I cannot determine for you what that logic may be.)
SET #SQL = 'UPDATE Source_Table' + #BR
+ 'SET __ACTIVE = CASE WHEN rn = 1 THEN 1 ELSE 0 END' + #BR
+ 'FROM ( ' + #BR + 'SELECT ROW_NUMBER() OVER (PARTITION BY '
+ #Source + ' ORDER BY __REC_ID DESC) AS rn
, * FROM ' + #DatabaseName + '.' + #SchemaName + '.' + #TableName
+ #BR + ') Source_Table' + #BR
+ ' WHERE SOMECONDITION = SOMECONDITION'

Dynamic SQL error converting nvarchar to int

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.

Script all data out of a single table or all tables in Sql Server 2005/8?

Simple question here, and I know there are lots of kludgy ways to do it :)
But I was hoping a SQL server expert had a script to easily script all the data out of a table? Or possibly all the tables in a DB?
I'm getting tired of copying and pasting data over RDC! :)
I had a similar requirement.
To script insert statements for each row in a table. Sometimes I needed the Identity column, other times I did not.
So, I wrote this:
CREATE PROCEDURE [dbo].[GenerateInsertScripts] (
#tableName varchar(100),
#tableSchema varchar(50) = 'dbo',
#skipIdentity bit = 1
)
AS
BEGIN
DECLARE #columnName varchar(800)
DECLARE #columnType varchar(20)
DECLARE #statementA varchar(MAX)
DECLARE #statementB varchar(MAX)
DECLARE #statement nvarchar(MAX)
DECLARE #isIdentity bit
DECLARE #commaFlag bit
SET #statementA = 'INSERT INTO [' + #tableSchema + '].[' + #tableName + '] ('
SET #statementB = ''' + '
DECLARE cols CURSOR FOR
SELECT
COLUMN_NAME,
DATA_TYPE,
(SELECT COLUMNPROPERTY(OBJECT_ID('[' + #tableSchema + '].[' + #tableName + ']'),
information_schema.columns.COLUMN_NAME, 'IsIdentity')) AS IsIdentity
FROM
information_schema.columns
WHERE
TABLE_NAME = #tableName
AND
TABLE_SCHEMA = #tableSchema
ORDER BY
ORDINAL_POSITION
OPEN cols
FETCH cols INTO #columnName, #columnType, #isIdentity
SET #commaFlag = 0
WHILE ##FETCH_STATUS = 0
BEGIN
IF NOT (#isIdentity = 1 AND #skipIdentity = 1) BEGIN
IF #commaFlag = 1 BEGIN
SET #statementA = #statementA + ', '
SET #statementB = #statementB + ' + '', '' + '
END
SET #commaFlag = 1
SET #statementA = #statementA + '[' + #columnName + ']'
SET #statementB = #statementB + 'CASE WHEN [' + #columnName + '] IS NULL THEN ''NULL'' ELSE ' +
CASE
WHEN #columnType = 'bigint' OR #columnType = 'int' OR #columnType = 'tinyint' OR #columnType = 'bit' THEN
'CAST([' + #columnName + '] AS varchar(MAX))'
WHEN #columnType = 'datetime' THEN
''''''''' + CONVERT(varchar, [' + #columnName + '], 121) + '''''''''
ELSE
''''''''' + REPLACE(CAST([' + #columnName + '] AS varchar(MAX)), '''''''', '''''''''''') + '''''''''
END
+ ' END'
END
FETCH cols INTO #columnName, #columnType, #isIdentity
END
SET #commaFlag = 0
CLOSE cols
DEALLOCATE cols
SET #statementB = #statementB + ' + '''
SET #statement = 'SELECT ''' + #statementA + ') VALUES (' + #statementB + ')'' [Statement] FROM [' + #tableSchema + '].[' + #tableName + ']'
EXEC sp_executesql #statement
END
use this great script:
http://vyaskn.tripod.com/code.htm#inserts
but this is a bad way to move data, good to help fix a problem or move a little test data etc.
If you are moving data to a differnt database or some sort of file, use SSIS. IF the task is not complex, use the wizard, right click onthe datbasename, go to taks and choose Export data.
If you are doing the whole database an alternative is to simply restore the latest backup in the other location.
For SQL Server 2008 you can use the Script Data option in the Generate Scripts wizard. Not for 2005 though.
You can start the wizard by right clicking on a database in the Object Explorer in SSMS, and then Tasks -> Generate Scripts....
For SQL Server 2005, you can use the free Database Publishing Wizard from Microsoft.