Using variable inside dynamic SQL - sql

I have the below SQL..What I am trying to do is use the Parameter defined at the stored procedure level inside dynamic SQL:
CREATE PROCEDURE [dbo].[Test]
(#DealID NVARCHAR(500),
#OUTPUT NVARCHAR(MAX) OUTPUT,
#FeeType CHAR(1)
) -- I want to use this parameter inside dynamic SQL query
AS
DECLARE #exec_str NVARCHAR(MAX)
DECLARE #ParmDefinition NVARCHAR(MAX)
BEGIN
SET #exec_str = N'DECLARE #ParmDefinition NVARCHAR(500)
SELECT * FROM #FeeType' --This is where I want to use the variable
DECLARE #ParamDefinition nvarchar(max)
SET #ParamDefinition = N'#OUTPUT NVARCHAR(MAX) OUTPUT'
EXEC sp_executesql #exec_str, #ParamDefinition
Can someone please tell me how to do it?
Thanks

In SQL Server Identifiers can't be parameterized.
Since you are using dynamic SQL anyway, you can do something like this:
SET #exec_str= N'Select * from '+ #FeeType
EXEC(#exec_str)
However, this is vulnerable to SQL injection attacks. To reduce the risk to minimum you should check first that such a table name exists, and I would also use quotename just to be on the safe side:
IF EXISTS
(
SELECT 1
FROM Information_Schema.Tables
WHERE TABLE_NAME = #FeeType
)
BEGIN
SET #exec_str= N'Select * from '+ QUOTENAME(#FeeType)
EXEC(#exec_str)
END

Related

Setting SQL Variable via Dynamic SQL

I know I am overthinking this, but I've been banging against this for too long so I'm reaching out for help.
This is the statement I'm trying to run: SELECT #cntMax = MAX(id) FROM [Raw_Item-FieldReport]
BUT, the table name is a variable #reportTable
This doesn't work:
SET #sql = 'SELECT #cntMax = MAX(id) FROM #reportTable'
EXEC sp_executesql #sql
I even tried having the actual table name in the SET #sql and that doesn't work either.
I didn't think it would be this difficult, please tell me I'm missing something easy/obvious.
Here's the full bit of code for those who want it:
DECLARE
#inTable nvarchar(255) = 'Raw_Item',
#reportTable nvarchar(255),
#fieldName nvarchar(255),
#cnt int,
#cntMax int,
#sql nvarchar(max)
SET #reportTable = #inTable + '-FieldReport'
SET #cnt = 1
SELECT #cntMax = MAX(id) FROM [Raw_Item-FieldReport]
PRINT #cntMax
SET #cntMax = 0
SET #sql = 'SELECT #cntMax = MAX(id) FROM [Raw_Item-FieldReport]'
EXEC sp_executesql #sql
PRINT #cntMax
SQL Server 12.0.2008.8 (on Azure)
You need to use an output parameter, otherwise SQL Server has no idea how to connect #cntMax in the dynamic SQL to #cntMax not in the dynamic SQL, since they are different scopes. And to protect yourself from SQL injection (some tips here and here), always check that your object exists, and use QUOTENAME() as opposed to manually adding square brackets (and you should always use QUOTENAME() when building object names from user input or variables, even when they don't have bad characters like dashes):
DECLARE #sql nvarchar(max),
#inTable nvarchar(255) = N'Raw_Item',
#reportTable nvarchar(255);
SET #reportTable = N'dbo.' + QUOTENAME(#inTable + '-FieldReport');
IF OBJECT_ID(#reportTable) IS NOT NULL
BEGIN
SET #sql = N'SELECT #cntMax = MAX(id) FROM ' + #reportTable + N';';
EXEC sys.sp_executesql #sql,
N'#cntMax int output',
#cntMax = #cntMax OUTPUT;
PRINT #cntMax;
END
ELSE
BEGIN
PRINT 'Nice try, h#xx0rs!';
END
Always use schema reference (dbo), always use statement terminators, and please try to avoid naming things with invalid identifier characters like dash (-). And one additional tip: always use N prefix on N'nvarchar string literals'.

Dynamic SQL with parameters

I have a SQL query stored in a table that contains parameter names. I need to know how to execute it properly in a stored procedure.
This is my SQL code in the procedure
PROCEDURE [spMassUpdateSKUs]
#SKU AS NVARCHAR(20)
,#FIELD AS NVARCHAR(50)
,#VALUE AS NVARCHAR(50)
,#RESULT as Int = Null Output
AS
BEGIN
IF EXISTS(SELECT CODENUMBER FROM INVENTORY_MASTER WHERE CODENUMBER=#SKU)
BEGIN
DECLARE #SQLQUERY AS NVARCHAR(50)
SET #SQLQUERY=(SELECT SQLUPDATE FROM MASS_UPDATE WHERE DROPDOWNLABEL=#FIELD)
EXEC SP_EXECUTESQL #SQLQUERY
END
and this is the sql query from the table
update inventory_master_flex set departmentid=#value where codenumber=#sku
I've tried replacing with the real parameters but that doen't work.
SELECT REPLACE(REPLACE(#SQLQUERY,'#VALUE',#VALUE),'#SKU',#SKU)
-- 50 is too short for sure; you may try 1000 or different number
DECLARE #SQLQUERY AS NVARCHAR(MAX)
-- for debug purpose
PRINT #SQLQUERY
-- params
EXEC SP_EXECUTESQL #SQLQUERY, N'#Value NVARCHAR(50), #sku NVARCHAR(50)`, #Value, #sku
REPLACE is not good in case of strings with quotes and so on which would brake the #sqlquery code.
Pass the parameters in using sp_executesql, not replace():
IF EXISTS(SELECT CODENUMBER FROM INVENTORY_MASTER WHERE CODENUMBER=#SKU)
BEGIN
DECLARE #SQLQUERY AS NVARCHAR(MAX);
SET #SQLQUERY = (SELECT SQLUPDATE FROM MASS_UPDATE WHERE DROPDOWNLABEL = #FIELD);
EXEC SP_EXECUTESQL #SQLQUERY, N'#SKU VARCHAR(255), #VALUE VARCHAR(255)', #SKU = #SKU, #VALUE = #VALUE
END;
I don't know what the types are. But if one or both are strings or dates, then you would need single quotes in your implementation. However, you are already using sp_executesql so go whole-hog and pass in parameters as well.

Execute Stored Procedure from Stored Procedure w/ dynamic SQL capturing output

Inside a stored procedure (A) I need to call a stored procedure (X) inside a specific database and capture the output. X returns a single value.
From what I understand I need to provide the DB name of X to the stored procedure in A and I need to use dynamic SQL to build the query on execution targeting the desired database.
What am unable to figure out is how to capture output from X in A to work with the result.
You could use sp_executesql to dynamically call your nested Stored Procedure.
DECLARE #db AS SYSNAME
DECLARE #return_value AS INT
DECLARE #output_value AS INT
DECLARE #sql AS NVARCHAR(MAX)
-- Set your DB name
SET #db = N'mydb'
/*
Use sp_executesql to dynamically pass in the db and stored procedure
to execute while also defining the values and assigning to local variables.
*/
SET #sql = N'EXEC #rtn = ' + #db + '.dbo.[your_stored_procedure] #output OUTPUT'
EXEC sp_executesql #sql
, N'#rtn AS INT, #output AS INT OUTPUT'
, #return_value = #rtn
, #output_value = #output OUTPUT
Adding to the above answer, following is the way to call stored procedures dynamically by passing parameters.
DECLARE #SpName VARCHAR(1000)
SELECT #SpName = DeleteLiveSP FROM dbo.ArchivalInfo (NOLOCK) WHERE TableName = #TableName
DECLARE #SqlString nvarchar(2000)
DECLARE #ParamDef nvarchar(2000)
SET #SqlString = N'exec '+#SpName + ' #CriteriaParam'
SET #ParamDef = N'#CriteriaParam XML'
EXECUTE sp_executesql #SqlString ,#ParamDef, #CriteriaParam = #Criteria

Procedure to insert Xml Into Sql Server -- Must Declare Scalar Variable

I am using the below procedure to try and insert xml via the filepath into a xml column. I am getting an error must declare scalar variable for ForeignId. Is there a better way of doing what I am trying to do, or am I on the right path?
Here is the procedure
ALTER PROC [dbo].[InsertXml] #path nvarchar(100)
,#ForeignId uniqueidentifier
AS
BEGIN
SET NOCOUNT ON
DECLARE #SQL nvarchar(4000) =
'INSERT INTO XmlTable(XmlId
, ForeignId
, TestXml)
SELECT NEWID()
, #ForeignId
,* FROM OPENROWSET(
BULK ''' + #path + ''',
SINGLE_BLOB) AS x;'
EXECUTE(#SQL);
RETURN ##ERROR;
END
When you're executing the SQL statement using EXECUTE(SQL) it has no access to the #ForeignId value
One way to solve this is to use sp_excuteSQL and do this instead of EXECUTE(#SQL);
DECLARE #ParmDefinition nvarchar(500);
SET #ParmDefinition = N'#ForeignId uniqueidentifier';
EXECUTE sp_executesql #SQL, #ParmDefinition, #ForeignId ;
You could also just concatenate the #ForeignId to your sql string but I can't recall if there are issues with that when using a uniqueidentifier

Creating SQL table using dynamic variable name

I want to create backup SQL tables using variable names.
something along the lines of
DECLARE #SQLTable Varchar(20)
SET #SQLTable = 'SomeTableName' + ' ' + '20100526'
SELECT * INTO quotename(#SQLTable)
FROM SomeTableName
but i'm getting
Incorrect syntax near '#SQLTable'.
It's just part of a small script for maintence so i don't have to worry about injections.
DECLARE #MyTableName sysname;
DECLARE #DynamicSQL nvarchar(max);
SET #MyTableName = 'FooTable';
SET #DynamicSQL = N'SELECT * INTO ' + QUOTENAME(#MyTableName) + ' FROM BarTable';
EXEC sp_executesql #DynamicSQL;
Unfortunately, you can't use bind variables for table names, column names, etc. IN this case you must generate dynamic SQL and use exec.
DECLARE #Script NVARCHAR(MAX);
SET #Script = N'SELECT * INTO SomeTableName_' + N'20100526' + N' FROM SomeTableName';
EXEC sp_executesql #Script
I've left the date separate as I assume you want to calculate it for every run.
You should look into using synonyms:
-- Create a synonym for the Product table in AdventureWorks2008R2.
CREATE SYNONYM MyProduct
FOR AdventureWorks2008R2.Production.Product;
GO
-- Query the Product table by using the synonym.
USE tempdb;
GO
SELECT ProductID, Name
FROM MyProduct
WHERE ProductID < 5;
GO
http://msdn.microsoft.com/en-us/library/ms177544.aspx
DECLARE #MyTableName nvarchar(20);
DECLARE #DynamicSQL nvarchar(1000);
SET #MyTableName = "FooTable";
SET #DynamicSQL = N'SELECT * INTO ' + #MyTableName + ' FROM BarTable';
exec #DynamicSQL;
this query is correct but just use single quote at the ("FooTable")='FooTable'