How do I pass a column as a parameter in Stored Procedure? - sql

How do I pass and use the column name to retrieve a bigint variable in the actual column?
DECLARE #personID BIGINT,
DECLARE #queryString varchar(500)
Set #queryString = 'Select #personID = ' + #PersonColumnID + ' from dbo.Loss_Witness where WitnessID = #witnessID'
exec(#queryString)
Error message states "Must declare variable '#personID'." I also tried
Set #queryString = 'Select ' + #personID + ' = ' + #witnessPersonID + ' from dbo.Loss_Witness where WitnessID = #witnessID'
And got the error message "Error converting data type varchar to bigint."
Any thoughts?

You need to specify that #personID is an out parameter with the OUTPUT keyword:
DECLARE #SQL NVARCHAR(max)
SET #SQL = 'SELECT #personID = w.' + #PersonColumnID + '
FROM dbo.Loss_Witness w
WHERE w.witnessID = #witnessID '
BEGIN
EXEC sp_executesql #SQL, #personID = #personID OUTPUT, #witnessID
END
There's another example here.

Also note that there is potentially an SQL Injection security hole in your code. If you're not sanitizing #PersonColumnID then you could be in big trouble.

Related

SQL Server - rename column within stored procedure with different databases

I have searched and searched and cannot work out how to resolve my problem. I am actually not sure it is possible but thought I'd post here and check.
The issue:
I have a stored procedure where I make the following call (there are 2 because I was trying different things but both fail):
SET #olddate = '1606'
SET #newdate = '1706'
SET #TableName = 'sometablename'
SET #sql = 'SP_RENAME ''[DBName' + #olddate + '.dbo.' + #TableName + #olddate +'].[ColumnName' + #olddate + 'restofname]'',''[ColumnName' + #newdate + 'restofname]'''
EXECUTE sp_executesql #sql
Other option:
SET #olddate = '1606'
SET #newdate = '1706'
SET #TableName = 'sometablename'
SET #old = '[DBName' + #olddate + '.dbo.' + #TableName + #olddate+']."[ColumnName' + #olddate + 'restofname]"'
SET #new = 'ColumnName' + #newdate + 'restofname'
EXECUTE sp_rename #objname = #old,#newname = #new, #objtype = 'COLUMN'
I saved this in a stored procedure, and then ran it in another query which has a different database context and got the following error for the first:
No item by the name of '[DBName.dbo.TableName.ColumnName]' could be found in the current database '[Other database]', given that #itemtype was input as '(null)'.
and this for the second:
Either the parameter #objname is ambiguous or the claimed #objtype (COLUMN) is wrong.
Now, what I'm wondering is: can I even do this? write a stored procedure that runs sp_rename in a table in one database and then call that stored procedure from another database?
I've also tried every permutation of putting open and closed brackets, [ and ] around parts of my old and new column names. Also tried putting N before the string. It's a bit of trial and error though and thus far nothing has worked.
You cannot put your DBName and schemaName and tablename into one bracket [ ] You need to enclose each with a bracket. And dont use brackets around you new name since it will take these brackets into the name. Its a string so you can just put spaces and what you like into your name.
declare #TableName nvarchar(max)
declare #ColumName nvarchar(max)
declare #NewColumn nvarchar(max)
declare #sql nvarchar(max)
SET #NewColumn = 'newtest'
SET #ColumName = 'test'
SET #TableName = 'trassor'
SET #sql = 'SP_RENAME ''[YOURDBNAME].[YOURSCHEMANAME].['+ #TableName +'].['+#ColumName+']'', '''+#NewColumn+''', ''COLUMN'''
/* SET #sql = 'SP_RENAME ''[LegogSpass].[dbo].['+ #TableName +'].['+#ColumName+']'', '''+#NewColumn+''', ''COLUMN''' */
PRINT #SQL
EXECUTE sp_executesql #sql
First of all use a select #sql query before the sp_executesql statement to view how the dynamic statement has been formed.
This gives you an understanding of the error.
In your case the brackets have been placed in a wrong way.
Either remove them or add individually to dbname,schemaname,tablename and column.
Also, you were missing the 'column' at the end of the sp_rename statement.
declare #sql nvarchar(max),#olddate varchar(10),#newdate varchar(10),#TableName varchar(100)
SET #olddate = '1606'
SET #newdate = '1706'
SET #TableName = 'sometablename'
SET #sql = 'SP_RENAME ''DBName' + #olddate + '.dbo.' + #TableName + #olddate +'.ColumnName' + #olddate + 'restofname'',''ColumnName' + #newdate + 'restofname'',''Column'''
select #sql
EXECUTE sp_executesql #sql
Thanks for all the help. I actually figured out the issue subsequently. I know the brackets were wrong but that wasn't the issue (as I tried all sorts of different combinations of brackets and none of them worked).
set #old = #TableName +'.[ColumnName' + #olddate + 'restofname]'
set #new = 'ColumnName' + #newdate + 'restofname'
execute ('use DBName exec sp_rename ''' + #old + ''', ''' + #new + ''', ''COLUMN''')
The trick was to include "Use Database" within the execute statement.

dynamic SQL update command

I am using MSSQL 2016,
I need to be able to update a row on a table dynamically.
I got a stored procedure :
CREATE PROCEDURE sp_lookupData_UpdatelookupValues
(
#FullTableName nvarchar(50),
#Id nvarchar(10),
#Name nvarchar(50),
#Description nvarchar(50)
)
AS
BEGIN
DECLARE #Cmd nvarchar(150) = N'UPDATE ' + #FullTableName + ' SET Name = ' + #Name + ', Description = ' + #Description + ' WHERE ID = ' + #Id + '';
EXECUTE sp_executesql #Cmd;
END
The problem is that Name and Description values are passed into the #Cmd like this :
UPDATE TABLE_NAME SET Name = Private, Description = Default WHERE ID = 1
Instead of 'Private' and 'Default'.
The result is an error where Private is being counted as a column which doesnt exist ( because of the bad format ).
Invalid column name 'Private'.
Put the quotes yourself
Use single quotes around Private and Default.
And since you are using dynamic querying, you have to double the single quotes to escape them.
DECLARE #Cmd nvarchar(150) = N'UPDATE ' + #FullTableName + ' SET Name = ''' + #Name + ''', Description = ''' + #Description + ''' WHERE ID = ' + #Id + '';
Also make sure you try the next solution, since the first one is SQL Injection compatible.
Use sp_executesql parameters
You can also use the parameters inside your #Cmd without doing the concatenation yourself but by passing the parameters to sp_executesql
Also I suggest you to QUOTENAME the #FullTableName parameter in case of spaces inside table's name.
DECLARE #Cmd nvarchar(150) = N'UPDATE QUOTENAME(#FullTableName) SET Name = #Name, Description = #Description WHERE ID = #Id;'
EXEC sp_executesql #Cmd, #FullTableName, #Name, #Description, #Id;
The advantage doing so, is you avoid any parameters not checked by the application to be able to do SQL Injection.
Reference :
https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-executesql-transact-sql
You have to add the quotes yourself. I prefer to use QUOTENAME to keep all the quotes recognizable:
QUOTENAME(#FullTableName, '''')
You cannot use parameters for identifiers. But, you can use parameters for values:
DECLARE #Cmd nvarchar(150) = N'
UPDATE ' + #FullTableName + '
SET Name = #Name,
Description = #Description
WHERE ID = #Id';
EXECUTE sp_executesql #Cmd,
N'#Name nvarchar(50), #Description nvarchar(50), #Id nvarchar(10)',
#Name = #Name, #Description = #Description, #Id = #id;
Unfortunately, the dynamic table name still poses risks, both in terms of SQL injection and syntax errors. I am guessing this is "controlled" code, not code with user input, so the risks might be acceptable. However, you probably should use quotename().
That brings up another issue which is probably the crux of the problem. Why do you have multiple tables with the same columns? Could these -- should these -- all be stored in a single table? This type of code calls into question aspects of the data model.

SQL Store a value from an EXECUTE Command into a variable

I'm attempting to store a value into a variable from a EXECUTE command. I know I'm suppose to use sp_executesql command, but all examples online are only making more confused. So here is what I'm trying to do.
I have a stored procedure that accepts two parameters (a table name, a room #). To have a dynamic table name, I use dynamic SQL style while using strings. I'm attempting to store a phone number that is either from multiple tables. I got this working so far.
DECLARE #Location varchar(MAX);
DECLARE #Room varchar(10);
DECLARE #Number char(8);
DECLARE #SQLString varchar(MAX);
SET #Location = N'CMPhone.dbo.GardenCottage';
SET #Room = N'202';
SET #SQLString ='SET #Number = (SELECT PhoneNumber FROM ' + #Location + ' WHERE Room = ''' + #Room + ''');';
PRINT(#SQLString);
OUTPUT
SET #Number = (SELECT PhoneNumber FROM CMPhone.dbo.GardenCottage WHERE Room = '202');
SET #Number = (SELECT PhoneNumber FROM CMPhone.dbo.GardenCottage WHERE Room = '202');
PRINT(#Number);
OUTPUT
123-4567
Which is the correct number. Now, here is where the problem comes in. I need to do another query using dynamic SQL so I can use multiple tables again. So in my stored procedure, I need to store my EXEC(#SQLString) into a variable (#Number) so I can use that value, and that's where I'm having problems. I can't get sp_executesql to store the value into #Number. The other query will look something like this
SET #SQLString = ' UPDATE PhoneNumbers SET Active = ''1'' WHERE
PhoneNumber = ''' + #Number + ''';';
EXEC(#SQLString);
If this is confusing in anyway, or you have questions, please ask. Any help is very much appreciated. Thanks
Update #1:
I have this new string now
#SQLString = 'SELECT PhoneNumber FROM ' + #Location ' + ' WHERE Room = ''' + #Room + ''';';
EXECUTE SP_EXECUTESQL #SQLString
gets the correct number, but I don't know how to set up a OUTPUT parameter.
I'm attempting to follow this example from Microsoft
DECLARE #SQLString NVARCHAR(500)
DECLARE #ParmDefinition NVARCHAR(500)
DECLARE #IntVariable INT
DECLARE #Lastlname varchar(30)
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
SELECT #Lastlname
But I don't see how their declaring the lastlNameOUT variables.
use output variable in your EXECUTE sp_executesql like this:
EXECUTE sp_executesql #SQLString, N'#Number char(8) out',#Number out then you will get #Number value from inside dynamc sql, then you can use that value in other part of the query. hope this helps

SQL query error: Conversion failed when converting the varchar value

I search about the error, but I only find different answers that didn't work for me.
I have this query, in SQL Server:
DECLARE #column_name varchar (25),
#data_column int,
#table_name varchar (25)
DECLARE #mySql nvarchar (MAX)
SET NOCOUNT ON;
SET #column_name = 'Excellent'
SET #table_name = 'CSAT'
SET #data_column = 10
SET #mySql = 'INSERT INTO '+#table_name+'('+#column_name+') VALUES('+#data_column+')'
EXEC (#mySql)
When I execute it, it shows me this error:
Conversion failed when converting the varchar value 'INSERT INTO CSAT(Excellent) VALUES(' to data type int.
All the columns are Int and allow nulls.
I have to make a conversion or something? I appreciate your help!
#data_column is an int, so you need to convert it to varchar because you are building a string.
SET #mySql = 'INSERT INTO '+#table_name+'('+#column_name+')
VALUES('+ Convert(Varchar(10), #data_column)+')'
When sql server encounters an expression that mixes strings and int, it attempts to convert the string to an int (instead of the the other way around). This is documented here: SQL Server Data Type Precedence
add some space like this
SET #mySql = 'INSERT INTO '+#table_name+' ('+convert(varchar(50),#column_name)+') VALUES('+#data_column+')'
The sql is trying to make a sum of your string on line:
SET #mySql = 'INSERT INTO '+#table_name+'('+#column_name+') VALUES('+#data_column+')'
Change the #data_column parameter from int to varchar or use a CONCAT function to create your SQL command:
SET #mySql = CONCAT('INSERT INTO ',#table_name,' (',#column_name,') VALUES(',#data_column,')')
You should use Parametrised Query to do this, Also use appropriate datatype for object names...
something like this....
SET NOCOUNT ON;
DECLARE #column_name SYSNAME
,#data_column INT
,#table_name SYSNAME
,#mySql NVARCHAR(MAX);
SET #column_name = 'Excellent'
SET #table_name = 'CSAT'
SET #data_column = 10
SET #mySql = N' INSERT INTO '+ QUOTENAME(#table_name)
+ N' ('+ QUOTENAME(#column_name) +') '
+ N' VALUES( #data_column )'
Exec sp_executesql #mySql
,N'#data_column INT'
,#data_column

Difficulty using a CHAR in Dynamic SQL

I'm trying to pass in #accountType, a char value to a stored procedure that uses dynamic SQL. It is declared as char(4) in the procedure. The current error is Incorrect syntax near 'D' if I try to change it I get invalid column: D.
I cannot figure out how dynamic SQL wants me to indicate that the variable is a char. I've tried it many ways, here is the most recent:
set #q = 'Update ' + #statementTable +
' SET Account = '+ #padding + #accountNumber +
' WHERE ClosingDate BETWEEN CAST('''+CONVERT(VARCHAR(20),#proc_dateStart)+''' AS DATE) AND CAST('''+CONVERT(VARCHAR(20),#proc_dateEnd)+''' AS DATE)' +
' AND AccountType =' + ''''+ #accountType +''''
The value is coming from my C# code exactly like this: D
No single quotes or anything around the letter. Any ideas? I'm more than a bit stuck with this.
Something like this, you need to have the parameters actually within the string statement, then when you execute sp_executesql, you then pass what each of those parameters are.
DECLARE #q VARCHAR(MAX)
DECLARE #statementTable VARCHAR(50)
DECLARE #padding VARCHAR(50)
DECLARE #accountNumber CHAR(4)
DECLARE #proc_dateStart VARCHAR(50)
DECLARE #proc_dateEnd VARCHAR(50)
DECLARE #accountType VARCHAR(50)
SET #q = 'Update #statementTable
SET Account = ''#accountNumber''
WHERE ClosingDate BETWEEN CAST(''+CONVERT(VARCHAR(20),#proc_dateStart)+'' AS DATE) AND CAST(''+CONVERT(VARCHAR(20),#proc_dateEnd)+'' AS DATE)
AND AccountType = ''#accountType'''
EXEC sys.sp_executesql #sql, N'#statementTable VARCHAR(50),#accountNumber CHAR(4),#proc_dateStart VARCHAR(50), #proc_dateEnd VARCHAR(50),#accountType VARCHAR(50)',
#statementTable,#accountNumber,#proc_dateStart,#proc_dateEnd,#accountType;