Reputation: 651
I have a stored procedure that gets these parameters:
@TaskId UNIQUEIDENTIFIER,
@ColumnName VARCHAR(255) = NULL,
@CheckBoxValue VARCHAR(255) = NULL
Then I have an UPDATE
statement like this:
UPDATE [RedMarkItems]
SET @ColumnName = @CheckBoxValue
WHERE [TaskId] = @TaskId
If I run my stored procedure like:
exec usp_RedMarkItem_Insert
@TaskId = '82ab0c4b-9342-46fa-acbe-c00b87571bf9',
@ColumnName = Item7,
@CheckBoxValue = 1,
@CurrentUser = '6074caea-7a8e-4699-9451-16c2eaf394ef'
It does not affect table just says
Commands completed successfully
But values still the same, but if I replace values in UPDATE statement like
UPDATE [RedMarkItems]
SET Item7 = 1
WHERE [TaskId] = '82ab0c4b-9342-46fa-acbe-c00b87571bf9'
It works! Why is it not working if I use parameters? Regards
Upvotes: 1
Views: 2759
Reputation: 4501
What you did was set @ColumnName equal to the value in @CheckBoxValue 0 or more times (based on how many rows exist in the table). Likely not what you intended...
Instead, you will either want to use dynamic SQL (set @sql = 'UPDATE … ' + QUOTENAME(@ColumnName) + 'rest of sql'
) or otherwise build a case statement to handle each column based on the value you are trying to update dynamically. SQL needs to bind the statement at compile time, so you need to have that available at compile time for the query processor to make sure that the column is real, has the right types to do type derivation, etc. Using parameters as you did would prevent all of that logic from working (assuming the semantics were as you intended in your posted question).
Please be careful as SQL injection attacks are possible on non-validated parameters. You would want to make sure that the column name is a valid column name and not something that allows arbitrary SQL code execution under the context of the running transaction.
Upvotes: 2
Reputation: 95950
The mistake you're making here is you're under the impression that a variable/parameter can be used in place for an object's name. Simply put, it can't. An object's name must be a literal, so you can't do something like:
DECLARE @TableName sysname;
SET @TableName = N'MyTable';
SELECT *
FROM @TableName;
For things like this you need to use dynamic SQL, and (just as importantly) secure dynamic SQL.
Firstly, I would change @ColumnName
to the datatype sysname
(which is a synonym for nvarchar(128)
) and @Checkbox
to a bit
(A Checkbox can have only 3 values: True
/1
, False
/0
and NULL
, so an nvarchar(255)
is a very poor data choice). Then your Stored Procedure will look something like:
DECLARE @SQL nvarchar(MAX);
SELECT @SQL = N'UPDATE RedMarkItems' + NCHAR(10) +
N'SET ' + QUOTENAME(c.[name]) + N' = @Checkbox' + NCHAR(10) +
N'WHERE TaskID = @ID;'
FROM sys.tables t
JOIN sys.columns c ON t.object_id = c.object_id
WHERE c.[name] = @ColumnName
AND t.[name] = 'RedMarkItems';
EXEC sp_executesql @SQL, N'@Checkbox bit, @ID uniqueindentifier', @Checkbox = @CheckBoxValue, @ID = @TaskId;
The reason for the references to the sys
tables is to ensure that the column does indeed exist. If it does not, then no SQL will be run. This is just an extra safety measure, alongside the use of QUOTENAME
.
Upvotes: 5