Scope issue with stored procedure string parameter being returned from dynamic SQL - sql

I have a stored procedure for creating inventory transactions that requires the assembly of a description. Since other inventory stored procedures will also need to assemble their descriptions similarly, I am trying to create a helper stored procedure.
This helper will use standard parameters and construct the description. The trouble I am having is returning the string Description back to the inventory transaction.
A Inventory transaction calls the helper this way:
declare #TransDescription nvarchar(256)
declare #TransDescOut nvarchar(256)
EXEC [dbo].[sp_KF_Helpers_CreateInvTransDescription]
#TransactionTypeID, #UserName, #OwnerTypeID, #OwnerID,
#TransDesc = #TransDescOut OUTPUT
SET #TransDescription = #TransDescOut
Then I use #TransDescription as a value for inserting into column data.
The helper code is:
CREATE PROCEDURE [dbo].[sp_KF_Helpers_CreateInvTransDescription]
( #TransactionTypeID int,
#UserName nvarchar(256),
#OwnerTypeID int,
#OwnerID int,
#TransDesc varchar(256) OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
declare #rslt int = 0
declare #strTyepID varchar(256) = #TransactionTypeID
declare #strOwnerID varchar(256) = #OwnerID
declare #intOwnerTypeID int = #OwnerTypeID
declare #OwnerStr varchar(256) = 'KF_'
declare #OwnerIDStr varchar(256) = (select Description from KF_OwnerType where ID = #intOwnerTypeID)
select #OwnerStr = #OwnerStr + #OwnerIDStr
declare #sql1 nvarchar(4000)
Select #sql1 = 'Select Top 1 (a.Description + '' - '' + ' + #OwnerStr + '.Name) TransDesc
from KF_InventoryTransactionType a, KF_OwnerType c, ' + #OwnerStr + '
where a.ID = ' + #strTyepID + ' and '
+ #OwnerStr + '.ID = ' + #strOwnerID
exec SP_EXECUTESQL #sql1, N'#TransDesc varchar output ', #TransDesc output
End
As you can see, I am using dynamic SQL to generate the description. The problem is that the help code generates the correct description, but does not pass it back as output.
Anyone know why or where I am losing scope for returning my output description?

You forgot to assign the variable. Try:
select top 1 #TransDesc = a.Description + '' - '' + ' + #OwnerStr + '.Name
...
Also, change the part where you are declaring the parameter of the dynamic script (#TransDesc), or you will run into another issue. Currently the parameter is being declared like this:
#TransDesc varchar output
which is equivalent to
#TransDesc varchar(1) output
Most likely, it should be
#TransDesc varchar(256) output
instead.

Related

sql Append for value with single quotes - String Concatenation

This extended version on my previous question sql Append for value with single quotes which am trying to fix
DECLARE #fNAME varchar(40) = 'O'brain'
DECLARE #query nvarchar(256)
DECLARE #id nvarchar(5) = '8'
SET #query = 'UPDATE TableName SET columnname = ''' + #fNAME + '''' +'Added on '+ GETDATE() + ''' WHERE Id= ' + convert(nvarchar, #id)
am trying to get below output using
EXEC sp_executesql
Update table name set columnname = 'O''brain Added on Aug 8 2017 11:15AM' where id = 8
Basically am trying to append some text and current date along with name to be updates as text in one column
I would define and execute the query as:
DECLARE #fNAME varchar(40) = 'O''brain';
DECLARE #query nvarchar(256);
DECLARE #id nvarchar(5) = '8';
SET #query = '
UPDATE TableName
SET columnname = #fNAME + '' added on '' + convert(varchar(255), GETDATE() )
WHERE Id = #id';
EXEC sp_executesql #query, N'#fname varchar(40), #id nvarchar(5)', #fname=#fname, #id=#id;
Note: This is not going to format the date exactly as in your question. You haven't chosen a formatting for the date in your code, so I didn't either.
Here is how you would do this with a normal update. There appears to be no reason at all to use dynamic sql here.
DECLARE #fNAME varchar(40) = 'O''brain';
DECLARE #id nvarchar(5) = '8';
Update TableName
set columnname = #fNAME + ' added on ' + convert(varchar(255), getdate())

How to set dynamic query value to a variable in SQL Server stored procedure with parameters?

I want to set a variable to the value generated using the dynamic query outside the query.
I tried the sp_executesql concept, but this does not help me because I am using the parameter value in the dynamic query.
Are there any possibilities to fix this issue?
CREATE PROCEDURE [dbo].[SP_test_proc]
#id int = null,
#deptId int = null
As
BEGIN
DECLARE #Condition VARCHAR(MAX),#Query NVARCHAR(MAX)
SET #Condition= 'Where Id = '#id + case when #deptid is null then '' else #deptid End
SET #Query = 'Declare #Name varchar(100)
Set #Name = Select name from student '+ #Condition
SELECT *
FROM personal
WHERE name = #Name
END
Use output parameter in dynamic sql as shown here
Also see this
Try this :
DECLARE #Condition VARCHAR(MAX),#Query NVARCHAR(MAX)
SET #Condition= 'Where Id = '#id + case when #deptid is null then '' else #deptid End
Declare #Name varchar(100),#nameval varchar(100),#paramdef varchar(100)
SET #Query = '
Select #Name = name from student '+ #Condition
set #paramdef=N'#Name varhcar(20) OUTPUT'
execute sp_executesql #Query,#paramdef,#Name=#nameval Output
SELECT *
FROM personal
WHERE name = #nameval
Hope this code will work for you.
CREATE PROCEDURE [dbo].[SP_test_proc]
#id int = null,
#deptId int = null
As
BEGIN
DECLARE #Condition VARCHAR(MAX),#Query NVARCHAR(MAX)
SET #Condition= 'Where Id = ' + #id + ' And [Deptid] = ' + ISNULL(#deptid,'')
SET #Query = 'SELECT * FROM personal WHERE name IN ( SELECT name FROM student ' + #Condition + ')'
EXEC #Query
END
[Deptid] is not sure I don't know column name

How do I pass input parameters to sp_executesql?

In SQL Server 2014, I am trying to create a dynamic WHERE clause.
I have built the query as a string, but when I try to execute it with sp_executesql, I get the following error:
Líne 13 You must declare the scalar variable "#desde".
I can't figure out how to get sp_executesql to recognize the input parameters.
ALTER PROCEDURE [dbo].[seleccionarFacturas]
-- Add the parameters for the stored procedure here
#desde char(8) = null,
#hasta char(8) = null,
#minimo int = null,
#ciudad int = null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #tiendas varchar(max);
DECLARE #tablaFacturas TABLE
(
fecha char(8),
CO char(8),
consecutivo varchar(max),
nombreCliente varchar(max),
ventaTotal int
);
SET #tiendas='(ID_CO=20 OR ID_CO=22 OR ID_CO=23 OR ID_CO=27 OR ID_CO=35 OR ID_CO=39 OR ID_CO=45 OR ID_CO=48 OR ID_CO=55 OR ID_CO=58)';
DECLARE #dynamicCode nvarchar(max)=
N'
SELECT
FECHA_DCTO,
ID_CO,
DETALLE_DOC,
NOM_CLI_CONTADO,
(SUM(TOT_VENTA)) AS ventaTotal
FROM
moda.dbo.CMMOVIMIENTO_VENTAS
WHERE'
+ #tiendas +
N' AND FECHA_DCTO >= #desde
AND FECHA_DCTO <= #hasta
GROUP BY
DETALLE_DOC, ID_CO, FECHA_DCTO, NOM_CLI_CONTADO';
INSERT INTO #tablaFacturas
EXEC [dbo].[sp_executesql] #dynamicCode;
SELECT * FROM #tablaFacturas
Instead of
EXEC [dbo].[sp_executesql] #dynamicCode;
Use
EXECUTE sp_executesql #dynamicCode,
N'#desde char(8), #hasta char(8)',
#desde = #desde, #hasta = #hasta;
You have to define the parameters you used in the dynamic query like#desde and #hasta
Please refer sp_executesql
Else You can concat the values of #desde, #hasta to the dynamic query,
like
'....FECHA_DCTO >= ' + #desde +
'AND FECHA_DCTO <= ' + #hasta +
'GROUP BY ....'

Execute sp_executesql

Table called Emp contain id,name,lname,birthdate,address and salery .I want to select from emp.
Basic query : select * from emp
If pass the value for lname ,query : select * from emp where lname = 'fgfg' like this.
So I created following sp.
create Procedure Proc_selectEmp
(
#name varchar(10) = null,
#lname varchar(10) = null,
#id varchar(10) = null
)
as
begin
select * from Emp
where
(#name is null or name = #name)
and (#lname is null or lname = #lname)
and (#id is null or id = #id)
end
Like emp,there are 13 table having same column name.
So my tablenmae is also dynamic.That's why, I choose execute sp_executesql.Can I create like this
create Procedure Proc_selectEmp
(
#name varchar(10) = null,
#lname varchar(10) = null,
#id varchar(10) = null
#tableName varchar(30)
)
as
begin
declare #query nvarchar(1000)
set #query = #query +'select * from '+#tableName+'
where ('+#name+' is null or name = '+#name+')
and ('+#lname+' is null or lname = '+#lname+')
and ('+#id+' is null or id = '+#id+')
end'
execute sp_executesql #query
It will work, although is pretty smelly, given that it requires that it requires that table name is a variable and thus tables must have the same column definitions.
Also, rather than including the #param is null or column = #param, rather, leave out unnecessary filters entirely, which is easy to do since you are using dynamic sql. This will avoid the parameter sniffing problem.
Lastly, instead of appending the column filters into the string, rather use the parameterized overload of sp_executesql, which will protect your from SQL injection attacks, and handle the escaping of quotes etc for you. Unfortunately, #tablename can't be parameterized, but hopefully? this isn't a user or foreign-supplied variable (in which case you will need to do some more thinking about design and or validation techniques).
i.e.
declare #query nvarchar(max)
set #query = N'select * from ' + #tableName + N` where (1=1)`
if (#name is not null)
set #query = #query + N'and name = #name'
-- ... same for #id and #lname
exec sp_executesql #SQL,
N'#name varchar(10),
#lname varchar(10),
#id varchar(10)',
#name = #name,
#lname = #lname,
#id = #id
Edit
Re : securing un-parameterizable inputs like table or column names in dynamic sql - see this post here for ideas - use of QUOTENAME and white-listing column / table names are prominent.
Yes you can but you have to write
EXECUTE(#query)
Instead of
execute sp_executesql #query

Ad Hoc query assigns results to local variables

Hi is it possible for me to do this somehow? When i run the statement i get an exception, #Price_Plan is not delared, so obviously the adhoc query does not have scope to access #Price_Plan. Is there a workaround, or a better way to query a table whose name changes per execution of this query.
DECLARE #Price_Plan varchar(3), #MNP_Network varchar(3), #GSM_Code varchar(3), #GEO_Dist varchar(6),
#Call_ProdNo varchar(7), #Call_Time datetime, #CallId int, #dtl_call_dur int,
#Volume varchar(10), #Call_Cost int
--Assume CallId has a value
SET #Sql =
'SELECT
#Price_Plan = Price_Plan, #MNP_Network = MNP_Network, #GSM_Code = GSM_Code, #GEO_Dist = GEO_Dist,
#Call_ProdNo = Call_ProdNo, #Call_Time = Call_Time, #dtl_call_dur = dtl_call_dur,
#Volume = Volume
FROM ' + #TableName + '
WHERE CallId = ' + CONVERT(varchar(10),#CallId) + ''
PRINT #SQL
EXEC (#Sql)
Are you sure this is this error? I tested that query and it returned two errors:
- Not declared #TableName
- Not declared #Sql
When you declare those variables it should work good.
But better way is to use sp_executesql.
An example:
EXEC sp_executesql
N'SELECT * FROM AdventureWorks.HumanResources.Employee
WHERE ManagerID = #level',
N'#level tinyint',
#level = 109;
First argument is the query with parametes, 2nd argument - parameters names with types separated by commas, and then goes the actual values of the parameters.
EDITED:
Here is the another example using OUTPUT parameter:
DECLARE #retCnt INT
EXEC sp_executesql
N'SELECT #retCnt = COUNT(*) FROM sys.tables',
N'#retCnt INT OUTPUT',
#retCnt = #retCnt OUTPUT
SELECT #retCnt
SELECT return 5 on my computer.