Add column name as parameter in stored procedure not working - sql-server

I have a stored procedure where I want to add a column name as a parameter.
The procedure updates some columns like this.
UPDATE Reservedele
SET Bemærkning = #Bemærkning
,Art = #Art
,Type = #Type
,Lev = #Lev
,#stockNumber = #stockCount
WHERE Varenummer = #Varenummer
I want to update a column named as stock1 as I pass in as #StockNumber, but all it does is setting #stockNumber equal to #Varenummer. (Which probably what I am telling it to do).
How can I do this?
I am new to SQL and I need to read up on dynamic SQL.
My full code looks like this, but the update statement is not doing anything.
ALTER PROCEDURE [dbo].[updateItemsInSqlNoImage]
#Bemærkning nvarchar(200),
#Varenummer nchar(20),
#Art nvarchar(50),
#Type nvarchar(50),
#Lev nvarchar(50),
#Bruger nvarchar(50),
#Dato nvarchar(50),
#Tid nvarchar(50),
#Ip nvarchar(50),
#stockCount int,
#stockNumber varchar(10)
as
BEGIN
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = N'UPDATE Reservedele' + #CRLF+
N',Bemærkning=#Bemærkning'+ #CRLF+
N',Art=#Art' + #CRLF+
N',Type=#Type' + #CRLF+
N',Lev=#Lev'+ #CRLF+
N' ,' + QUOTENAME(#stockNumber) + N' = #stockCount' + #CRLF +
N'WHERE Varenummer=#Varenummer;';
INSERT INTO Ændrede_dele(Varenummer,Dato,Tid,Bruger,Ip)
VALUES (#Varenummer,#Dato,#Tid,#Bruger,#stockNumber)
END

That isn't how SQL works, it isn't a scripting language, it's a query language. A variable cannot be used to replace a something that needs to be a literal value. #stockNumber is being seen as the parameter #stockNumber, not the value of the parameter, #stockNumber. As a result it makes it look like you have a SET operation that is part of an UPDATE but also trying to assign the value of variables/parameters (which you can't do).
You would need to use dynamic SQL to achieve this:
--All data types are guessed, replace appropriately
CREATE PROC dbo.YourProc #Bemærkning varchar(10),
#Art int,
#Type int,
#Lev decimal(6,2),
#stockNumber sysname, --apart from this one, this is correct
#stockCount int,
#Varenummer varchar(25) AS
BEGIN
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = N'UPDATE Reservedele' + #CRLF +
N'SET Bemærkning = #Bemærkning' + #CRLF +
N' ,Art = #Art' + #CRLF +
N' ,Type = #Type' + #CRLF +
N' ,Lev = #Lev' + #CRLF +
N' ,' + QUOTENAME(#stockNumber) + N' = #stockCount' + #CRLF +
N'WHERE Varenummer = #Varenummer;';
--PRINT #SQL; --Your debugging friend
--Again, all data types are guessed, replace appropriately
EXEC sys.sp_executesql #SQL, N'#Bemærkning varchar(10), #Art int, #Type int, #Lev decimal(6,2), #stockCount int, #Varenummer varchar(25)', #Bemærkning, #Art, #Type, #Lev, #stockCount, #Varenummer;
END;

Related

SQL Server Dynamic creation of global temp table and insert data issue

My requirement is to create a global temporary table and store data there which I would access later. I give a dynamic name to my global temporary table and getting error that
Invalid object name '##Tmp1_84'.
84 is #SPID
here is my script. please have a look and tell me what to rectify in code to get rid of runtime error Invalid object name '##Tmp1_84'
CREATE Proc USP_GetValuationValue
(
#Ticker VARCHAR(10),
#ClientCode VARCHAR(10),
#GroupName VARCHAR(10)
)
AS
DECLARE #SPID VARCHAR(MAX)
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SELECT #SPID=CAST(##SPID AS VARCHAR)
SET #SQL = N'SELECT * INTO ##Tmp1_'+#SPID+' FROM (SELECT min(id) ID,f.ticker,f.ClientCode,f.GroupName,f.RecOrder,' + STUFF((SELECT N',' + #CRLF + N' ' +
N'MAX(CASE FieldName WHEN ' + QUOTENAME(FieldName,'''') + N' THEN FieldValue END) AS ' + QUOTENAME(FieldName)
FROM tblValuationSubGroup g
WHERE ticker=#Ticker AND ClientCode=#ClientCode AND GroupName=#GroupName
GROUP BY FieldName
ORDER BY MIN(FieldOrder)
FOR XML PATH(''),TYPE).value('(./text())[1]','nvarchar(MAX)'),1,10,N'') + #CRLF +
N'FROM (select * from tblValuationFieldValue' + #CRLF +
N'WHERE Ticker = '''+#Ticker+''' AND ClientCode = '''+#ClientCode+''' AND GroupName='''+#GroupName+''') f' + #CRLF +
N'GROUP BY f.ticker,f.ClientCode,f.GroupName,f.RecOrder) X';
--EXEC sys.sp_executesql #SQL
EXEC(#SQL)
EXEC('select * from ##Tmp1_'+#SPID+' ORDER BY Broker')
EXEC('DROP TABLE IF EXISTS ##Tmp1_'+#SPID)
George has mentioned why your attempt doesn't work in their answer, so I'm not going to touch on that.
I'm instead going to fix the problem, which I touch on in my comments. In truth, there is no need for a (global) temporary table, you just SELECT ... INTO it and then SELECT from it; you make no further transformations making it pointless. AS such you could just SELECT the data in the first place, no temporary table needed.
I also fix your injection issue; this is a fatal flaw. Dynamic SQL accepts parameters and you using them is a must; not using parameters and instead using injection opens you up to all sorts of errors and security issues.
I can't test this, but I suspect this will work. If not, use your best friend to debug and propagate fixes to the dynamic SQL on any bits I've missed (or provide code we can run above with an MRE).
CREATE Proc USP_GetValuationValue
(
#Ticker VARCHAR(10),
#ClientCode VARCHAR(10),
#GroupName VARCHAR(10)
)
AS
BEGIN
DECLARE #SPID VARCHAR(MAX), --Is this even used now?
#SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SELECT #SPID=CAST(##SPID AS VARCHAR);
SET #SQL = N'SELECT * FROM (SELECT min(id) ID,f.ticker,f.ClientCode,f.GroupName,f.RecOrder,' + STUFF((SELECT N',' + #CRLF + N' ' +
N'MAX(CASE FieldName WHEN ' + QUOTENAME(FieldName,'''') + N' THEN FieldValue END) AS ' + QUOTENAME(FieldName)
FROM tblValuationSubGroup g
WHERE ticker=#Ticker AND ClientCode=#ClientCode AND GroupName=#GroupName
GROUP BY FieldName
ORDER BY MIN(FieldOrder)
FOR XML PATH(''),TYPE).value('(./text())[1]','nvarchar(MAX)'),1,10,N'') + #CRLF +
N'FROM (select * from tblValuationFieldValue' + #CRLF +
N'WHERE Ticker = #Ticker AND ClientCode = #ClientCode AND GroupName= #GroupName) f' + #CRLF +
N'GROUP BY f.ticker,f.ClientCode,f.GroupName,f.RecOrder) X' + #CRLF +
N'ORDER Y Broker;';
--PRINT #SQL; --YOur best friend
--EXEC sys.sp_executesql #SQL --Why did you comment this out? This is correct!
EXEC sys.sp_executesql #SQL, N'#Ticker varchar(10), #ClientCode varchar(10), #GroupName varchar(10)', #Ticker, #ClientCode, #GroupName
END;

Writing dynamic SQL in SQL Server 2016 throwing error

This is my stored procedure in SQL Server 2016:
CREATE PROCEDURE [USA_PHILIPS].[usp_stock]
#VCM INT,
#ID VARCHAR,
#SCHEMA_NAME VARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
DROP TABLE IF EXISTS [USA_PHILIPS].[stock]
SELECT STOCKNUMBER,STOCKBOOKS
INTO [USA_PHILIPS].[stock]
FROM [USA_PHILIPS].[DMARTSTOCK]
WHERE VCM = #VCM
AND ID = #ID
END
How can I pass schema name as a parameter #SCHEMA_NAME?
And execute these statements as dynamic SQL:
DROP TABLE IF EXISTS [USA_PHILIPS].[stock]
Please help.
I would personally do it this way, injecting the value directing into the dynamic query. I also fix some of your data types:
CREATE PROCEDURE [USA_PHILIPS].[usp_stock] #VCM int,
#ID varchar(25), --Always define your varchar lengths
#SCHEMA_NAME sysname --Correct data type for object names
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10)
SET #SQL = N'DROP TABLE IF EXISTS ' + QUOTENAME(#SCHEMA_NAME) + N'.[stock];' + #CRLF + #CRLF +
N'SELECT STOCKNUMBER,STOCKBOOKS' + #CRLF +
N'INTO ' + QUOTEMANE(#SCHEMA_NAME) + N'.[stock]' + #CRLF +
N'FROM [USA_PHILIPS].[DMARTSTOCK]' + #CRLF +
N'WHERE VCM=#VCM' + #CRLF +
N' AND ID = #ID;';
EXEC sys.sp_executesql #SQL, N'#VCM int, #ID varchar(25)', #VCM, #ID;
END;
All the code needs to be dynamic if an identifier is dynamic -- and you have to munge query strings:
CREATE PROCEDURE [USA_PHILIPS].[usp_stock] (
#VCM INT,
#ID VARCHAR(255),
#SCHEMA_NAME VARCHAR(50)
) AS
BEGIN
DECLARE #sql = NVARCHAR(MAX);
SET #sql = 'DROP TABLE IF EXISTS [SCHEMA].[stock]';
SET #sql = REPLACE(#sql, '[SCHEMA]', QUOTENAME(#SCHEMA_NAME));
EXEC sp_executeSQL #sql;
SET #sql = '
SELECT STOCKNUMBER, STOCKBOOKS
Into [SCHEMA].[stock]
from [USA_PHILIPS].[DMARTSTOCK]
WHERE VCM=#VCM AND ID = #ID';
SET #sql = REPLACE(#sql, '[SCHEMA]', QUOTENAME(#SCHEMA_NAME));
EXEC sp_executesql #sql,
N'#vcm INT, #id VARCHAR(255)',
#vcm=#vcm, #id=#id;
END;
Note some important changes to the query:
#ID has a length as an argument. This is important because the default varies by context and it might (well probably isn't) long enough for what you want.
I assume that you want the same table referenced in the DELETE as the INTO.
Pass the constant values as parameters.

Update a field using stored procedure, where the value for the field contains multiple ' '

I have a stored procedure where I am trying to update a column with the content of #Value.
This is my stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [PowerApp].[UpdateTableRequestSingle]
(#Solution nvarchar(100),
#ColumnName nvarchar(100),
#Value nvarchar(255),
#Record nvarchar(100),
#CreatedBy nvarchar(50),
#ExecutionFlag nvarchar(1))
AS
BEGIN
DECLARE #CRLF VARCHAR(2) = CHAR(13) + CHAR(10) --Linebreak
DECLARE #SQLStatement VARCHAR(MAX)
SET #SQLStatement = 'UPDATE [Extract' + #Solution + '].[TableRequest]' + #CRLF +
'SET [' + #ColumnName + '] = ' + '''' + #Value + '''' + #CRLF +
'WHERE TableName = ' + '''' + #Record + ''''
DECLARE #SQLStatementHistoryLog VARCHAR(MAX)
SET #SQLStatementHistoryLog = 'INSERT INTO [Extract' + #Solution + '].[TableRequestHistoryLog] ([SQLStatement], [CreatedBy])' + #CRLF +
'VALUES(''' + REPLACE(#SQLStatement,'''','''''') + ''',''' + #CreatedBy + ''')'
IF #ExecutionFlag = '0'
BEGIN
PRINT #SQLStatement
PRINT #SQLStatementHistoryLog
END
IF #ExecutionFlag = '1'
BEGIN
BEGIN TRANSACTION;
EXEC (#SQLStatement)
EXEC (#SQLStatementHistoryLog)
COMMIT TRANSACTION;
END
END;
And I am executing it with this:
/*
[PowerApp].[UpdateTableRequestSingle]
#Solution = 'SF',
#ColumnName = 'TableDataRequestWhereCustomDescription',
#Value = 'CRM_CP_Owner_Company_Number__c IN ('190', '230', '440', '450', '480')',
#Record = 'Account',
#CreatedBy = 'DKRAH',
#ExecutionFlag = 1
*/
If I pass
#Value = 'CRM_CP_Owner_Company_Number__c IN (190, 230, 440, 450, 480)'
it works but I want it to work with
#Value='CRM_CP_Owner_Company_Number__c IN ( '190', '230', '440', '450', '480')'
Does anyone know how to edit the T-SQL to make it work.
You don't escape the single quotes in #Value and therefore end up with unescaped single quotes in you dynamic query.
You should use parameterization using sp_executesql. You should also use quotename() to get the correctly quoted identifiers.
...
SET #SQLStatement =
'UPDATE ' + quotename('Extract' + #Solution) + '.[TableRequest]' + #CRLF +
'SET ' + quotename(#ColumnName) + ' = #Value' + #CRLF +
'WHERE TableName = #Record';
...
EXECUTE sp_executesql #SQLStatement, '#Value nvarchar(255), #Record nvarchar(255)', #Value = #Value, #Record = #Record;
...
(And analog for #SQLStatementHistoryLog)
for #ColumnName and maybe even #Solution you should better use the type sysname which is the type for identifiers.

Inserting stored procedure parameter to table only inserts first letter

I have stored procedure where I have parameter with datatype sql_variant. This parameter is then converted and inserted into parameter that is nvarchar(MAX) datatype. Inserting dates and floats are working fine. Then as example inserting into varchar(60) cell doesn't seem to work and only inserts first letter. When I add SELECT statements for the parameters in stored procedure it shows after executing the information to be inserted correctly and it only fails the actual insertion to table.
How to insert whole nvarchar to varchar(60) or similar cell?
Here are important parts of the code without too much extra:
CREATE PROCEDURE proc_name
#param1 nvarchar(30),
#param2 nvarchar(30),
#param3 sql_variant
AS
BEGIN
SET NOCOUNT ON;
DECLARE #update_param nvarchar(MAX);
SET #update_param = CONVERT(nvarchar(MAX), #param3);
-- Lots of not important stuff here such as getting datatype from INFORMATION_SCHEMA
DECLARE #Sql nvarchar(MAX);
SET #Sql = N' DECLARE #variable ' + QUOTENAME(#datatype) + N' = #update_param '
+ N' UPDATE table_name'
+ N' SET ' + #param1 + N' = #variable '
+ N' WHERE something = ' + #param2
Exec sp_executesql #Sql, N'#update_param nvarchar(MAX)', #update_param
Adding SELECT #Sql to the procedure gives following result:
DECLARE #variable [varchar] = #update_param
UPDATE table_name
SET column_name = #variable
WHERE something = thingsome
When #param1 = column_name, #param2 = thingsome
Edit: I read multiple questions on this topic and they all told to declare nvarchar length. Here I have it declared as nvarchar(MAX).
Edit2: Added code bits.
Edit3: After adding code and help in comments the answer is that there is length undeclared for #datatype in #Sql
This doesn't answer the question at hand, however, the SP you have is open to injection. Raw string concatenation like that is a dangerous game to play. This is far safer:
CREATE PROCEDURE proc_name
#param1 nvarchar(30),
#param2 nvarchar(30),
#param3 sql_variant
AS
BEGIN
SET NOCOUNT ON;
DECLARE #update_param nvarchar(MAX);
SET #update_param = CONVERT(nvarchar(MAX), #param3);
-- Lots of not important stuff here such as getting datatype from INFORMATION_SCHEMA
DECLARE #Sql nvarchar(MAX);
SET #Sql = N' DECLARE #variable ' + QUOTENAME(#datatype) + N' = #dupdate_param' --Where is the value of #datatype coming from?
+ N' UPDATE table_name'
+ N' SET ' + QUOTENAME(#param1) + N' = #variable '
+ N' WHERE something = #dparam2;'
Exec sp_executesql #Sql, N'#dupdate_param nvarchar(MAX), #dparam2 nvarchar(30)',#dupdate_param = #update_param, #dparam = #param2;
GO

STORED PROD FOR UPDATE SQL QUERY WHERE COLUMN NAME IS DYNAMIC

Lets say I want to write a stored prod
For SELECT
I found the above link for SELECT, but I want to do is for UPDATE:
SpUpdate #TableName varchar(50), #ColumnName varchar(50), #Value int, #Condition int
AS
BEGIN
UPDATE #Tablename
SET #ColumnName = #Value
Where PrimaryColName = #Condition
END
I know above code wont run. I know you can write a Dynamic code but I am not interested in Dynamic code. Can anyone help with different approch. Maybe using case statement or If statemens.
CREATE PROCEDURE SpUpdate
#TableName SYSNAME
, #ColumnName SYSNAME
, #Value INT
, #Condition INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N' UPDATE ' + QUOTENAME(#TableName)
+ N' SET ' + QUOTENAME(#ColumnName) + N' = #Value '
+ N' WHERE PrimaryColName = #Condition '
EXECUTE sp_executesql #Sql
,N'#Value INT, #Condition INT'
,#Value
,#Condition
END

Resources