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 ....'
Related
I'm looking for way to read data from user define type variable and when I do that:
I get this error:
Must declare the scalar variable "#mytabe".
Code:
CREATE TYPE CD_info AS TABLE
(
sestem_id nvarchar(10),
national_id nvarchar(14),
employee_name nvarchar(80),
salary money,
department nvarchar(80)
)
CREATE PROCEDURE Insert_CDInfo
#mytabe CD_info READONLY ,
#monthes int,
#TBname NVARCHAR(50)
AS
BEGIN
DECLARE #insertSql VARCHAR(MAX) = 'insert into' + #TBname +'(sestem_id, national_id,employee_name,salary,department)'+
'select top (1000000) sestem_id,national_id,employee_name,salary,department from '+ [#mytabe]
EXEC #insertSql
END
Thanks
The table valued parameter is only available in the scope in which it is declared so you cannot use it within the dynamic SQL EXECUTE statement.
Instead of EXECUTE, use a parameterized SQL statement and execute with sp_executesql. This will allow you to pass the TVP to the inner scope of the dynamic SQL as a parameter. For example:
CREATE PROCEDURE Insert_CDInfo
#mytabe CD_info READONLY ,
#monthes int,
#TBname NVARCHAR(50)
AS
BEGIN
DECLARE #insertSql nvarchar(MAX) =
N'INSERT INTO ' + QUOTENAME(#TBname) +' (sestem_id, national_id,employee_name,salary,department) '
+ N'SELECT TOP (1000000) sestem_id,national_id,employee_name,salary,department FROM #mytabe;';
EXEC sp_executesql
#insertSql
, N'#mytabe CD_info READONLY'
, #mytabe = #mytabe;
END
GO
I am trying to write a dynamic query. Let's say i have a table like below, which represents the hierarchy level of a sales agent:
AgentNumber Level1Agent Level2Agent Level3Agent Level4Agent Level5Agent
1122334455 1122334499 1122334488 1122334477 1122334466 1122334455
I want to be able to dynamically select a level based on a specified agent. My EXECUTE statement seems to work correctly, but how do I get the result stored in a variable I can use later? Every answer I have found seems to only get me a success return variable, not the actual query result.
Below is my code:
DECLARE #level INT = 1;
DECLARE #agent CHAR(10) = 1122334455;
DECLARE #colname NVARCHAR(11) = CONCAT('Level',#level,'Agent');
DECLARE #whereclause NVARCHAR(35) = CONCAT('WHERE AgentNumber = ',#agent);
DECLARE #qry NVARCHAR(300) = 'SELECT ' + #colname + ' FROM dbo.TABLE ' + #whereclause;
DECLARE #up NVARCHAR(10);
EXECUTE sp_executesql #qry, #up OUT
SELECT #up
The output of #up is NULL. If I change the last two lines to:
EXECUTE #up = sp_executesql #qry
SELECT #up
Now the output of #up is 0.
I want the output of 1122334499 and I need it stored in a variable that can later be used and inserted into a table.
Here is a fully functional example of how you can do this. Notice this is using a parameterized where clause and quotename around the column name in the dynamic sql to prevent sql injection.
if OBJECT_ID('tempdb..#Agents') is not null
drop table #Agents
create table #Agents
(
AgentNumber char(10)
, Level1Agent char(10)
, Level2Agent char(10)
, Level3Agent char(10)
, Level4Agent char(10)
, Level5Agent char(10)
)
insert #Agents
select '1122334455', '1122334499', '1122334488', '1122334477', '1122334466', '1122334455'
DECLARE #level INT = 3;
DECLARE #agent CHAR(10) = 1122334455;
DECLARE #colname NVARCHAR(11) = CONCAT('Level',#level,'Agent');
declare #agentout char(10)
DECLARE #qry NVARCHAR(300) = 'SELECT #agent_out = ' + quotename(#colname) + ' FROM #Agents WHERE AgentNumber = #agentin';
EXECUTE sp_executesql #qry, N'#agentin char(10), #agent_out char(10) output', #agentin = #agent, #agent_out = #agentout output
select #agentout
You can try this :
DECLARE #level INT = 1;
DECLARE #agent CHAR(10) = 1122334455;
DECLARE #colname NVARCHAR(11) = CONCAT('Level',#level,'Agent');
DECLARE #whereclause NVARCHAR(35) = CONCAT('WHERE AgentNumber = ',#agent);
DECLARE #qry NVARCHAR(300) = 'SELECT #agentout=' + #colname + ' FROM dbo.TABLE ' + #whereclause;
DECLARE #up NVARCHAR(10);
EXECUTE sp_executesql #qry, N'#agentout NVARCHAR(10) OUTPUT', #agentout=#up OUTPUT
SELECT #up
Create a variable table and makes your query insert the results you want there. Something like this:
declare #results table(field1 varchar(max), field2 varchar(max));
declare #sqlStatement varchar(max);
set #sqlStatement = 'insert into #results(field1, field2) select field1, field2 from table';
EXECUTE #sqlStatement;
select * from #results; --It will print the results from your sql statement!
I've written a stored procedure which is called on a link which provides a date value every time and #cg is NULL that time to filter the result on a particular date.
DECLARE #return_value int
EXEC #return_value = [dbo].[Get_Mydata]
#cg = NULL,
#tosearch = '15-05-2014'
SELECT 'Return Value' = #return_value
GO
And after first execution of the stored procedure, it gives some results and using same stored procedure.
I need to filter result by passing below parameter so this time #cg is NOT NULL.
DECLARE #return_value int
EXEC #return_value = [dbo].[Get_Mydata]
#cg = 'CUSTOMER NAME',
#tosearch = 'manish'
SELECT 'Return Value' = #return_value
GO
I'm not able to figure how should I create a dynamic where clause and add it to existing query as well as how to pass value to same parameter which already been passed as date.
More like first getting results for a particular date and then applying like filter on that result. I cannot pass different parameter that's Front end developers requirement.
This is my stored procedure and table data here. http://sqlfiddle.com/#!3/bb917
create proc Get_Mydata
(
#cg varchar(50),
#tosearch varchar(50)
)
as
begin
set nocount on
declare #sqlquery nvarchar(max)
set #sqlquery = N'select q_no, trandate, cust_name from testsp where CONVERT(Date, trandate, 103) = CONVERT(Date, ''' + #tosearch + ''' ,103)';
create table #temp1
(
q_no int,
trandate datetime,
cust_name varchar(50)
)
insert into #temp1(q_no, trandate, cust_name)
exec (#sqlquery)
select * from #temp1 as T;
set nocount off
end
What I have understood is that you want stored procedure to filter results on Date column when you pass null to #cg param and you want to filter results on Cust_name when you pass string 'Cust_Name' to your #Cg Param.
It should be fairly simple, But in any case you do not need a temp table to get the results back its just an over kill of a fairly simple query.
I would do something like this....
Pass the column name to #ColumnName Parameter, and your value to #tosearch parameter. It will build the query depending on what values you pass.
Make sure when you pass a value(Column Name) to #ColumnName.
create proc Get_Mydata
(
#ColumnName varchar(50),
#tosearch varchar(50)
)
as
begin
set nocount on;
declare #sqlquery nvarchar(max);
set #sqlquery = N' select q_no, trandate, cust_name '
+ N' from testsp '
+ N' where ' + QUOTENAME(#ColumnName) + N' = '
+ CASE
WHEN #ColumnName = 'trandate'
THEN N' CAST(#tosearch AS DATE)'
WHEN #ColumnName = 'cust_name'
THEN N' #tosearch'
ELSE N'' END
EXECUTE sp_executesql #sqlquery
,N'#tosearch varchar(50)'
,#tosearch
set nocount off;
end
I have a stored procedure that looks like this:
create stored procedure aaa
#columnName nvarchar(10),
#comparisonParam nvarchar(10),
#val nvarchar(100)
as
declare #date date
set #date = convert(#val, date)
exec('select * from Sheep where ' + #columnName + #comparisonParam + #date )
When actually the query is supposed to be like this:
select * from Sheep where birth_date = 12-12-2000
When I run the procedure it doesn't work with date value, but with string and int it works.
The date value must be quoted.
On a side note, I'd warn against doing this. If you need to build up dynamic sql you need to consider the risks such as: sql injection attacks, bad syntax, invalid semantics etc.
Consider using an existing component to build the query. A few examples:
.NET LINQ (to SQL/Entities) http://msdn.microsoft.com/en-us/library/bb397926.aspx
.NET SqlCommandBuilder http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommandbuilder.aspx
See Best way of constructing dynamic sql queries in C#/.NET3.5?
Your date literal needs to be surrounded in single quotes (I use CHAR(39) usually since it is easier to read and doesn't require escaping). Otherwise you are saying:
WHERE birth_date = (12) - (12) - (2000)
Which resolves to:
WHERE birth_date = -2000
Which resolves to DATEADD(DAY, -2000, '1900-01-01') or:
WHERE birth_date = '1894-07-11'
This is probably not going to yield the results you want.
With typical SQL injection warnings in place of course, and assuming that #columnName is always a string or date/time column, here is how I would re-write your stored procedure (though I would probably try to avoid the dynamic SQL altogether if I could).
ALTER PROCEDURE dbo.aaa
#columnName NVARCHAR(10),
#comparisonParam NVARCHAR(10),
#val NVARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'SELECT * FROM dbo.Sheep WHERE '
+ QUOTENAME(#columnName) + #comparisonParam + CHAR(39)
+ REPLACE(#val, CHAR(39), CHAR(39) + CHAR(39))
+ CHAR(39);
EXEC sp_executesql #sql;
END
GO
In order to thwart potential issues you may want to add validation for columns and data types, and ensure that the operation is one you expect. e.g.
CREATE PROCEDURE dbo.bbb
#columnName NVARCHAR(10),
#comparisonParam NVARCHAR(10),
#val NVARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #delimiter CHAR(1);
SELECT #delimiter = CASE
WHEN [system_type_id] IN
(104,48,52,56,127,59,60,62,106,108,122) THEN '' -- numeric
WHEN [system_type_id] IN
(35,40,41,42,43,58,61,99,167,175,231,239) THEN CHAR(39) -- string
END FROM sys.columns WHERE [object_id] = OBJECT_ID(N'dbo.Sheep')
AND name = #columnName;
IF #delimiter IS NULL
BEGIN
RAISERROR('Column ''%s'' was not found or an unexpected data type.', 11, 1,
#columnName);
RETURN;
END
IF #comparisonParam NOT IN (N'=', N'>=', N'<=', N'<', N'>', N'LIKE')
BEGIN
RAISERROR('Comparison param ''%s'' was not valid.', 11, 1, #comparisonParam);
RETURN;
END
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'SELECT * FROM dbo.Sheep WHERE '
+ QUOTENAME(#columnName) + ' ' + #comparisonParam + ' '
+ #delimiter + REPLACE(#val, CHAR(39), CHAR(39) + CHAR(39))
+ #delimiter;
EXEC sp_executesql #sql;
END
GO
Now make sure you use an unambiguous date format for your string literals. 12-12-2000 is not a good choice. 20001212 is much better.
There are possibly some ways to do this without dynamic SQL - I gave a very simplified answer here. This may be feasible depending on the data types, the number of potential columns, and the number of operations you want to support.
create stored procedure aaa
#columnName nvarchar(10),
#comparisonParam nvarchar(10),
#val nvarchar(100)
as
declare #date date
set #date = convert(#val, date)
exec('select * from Sheep where ' + #columnName + #comparisonParam + #date )
Build your dynamic SQL using a typed date parameter. Use sp_executesql which allows to pass parameter definitions and parameter values to the embedded SQL:
create procedure aaa
#columnName nvarchar(10),
#comparisonParam nvarchar(10),
#val nvarchar(100)
as
declare #date date, #sql nvarchar(max);
set #date = convert(#val, date);
-- Note how #date is a *variable* in the generated SQL:
set #sql =N'select * from Sheep where ' +
quotename(#columnName) + #comparisonParam + N'#date';
-- Use sp_executesql and define the type and value of the variable
exec sp_executesql #sql, N'#date date', #date;
You need to create table valued function for this rather than creating a stored procedure.
You can use any table valued function like
SELECT * from dbo.CallMyFunction(parameter1, parameter2
eg.
CREATE FUNCTION Sales.ufn_SalesByStore (#storeid int)
RETURNS TABLE
AS
RETURN
(
SELECT P.ProductID, P.Name, SUM(SD.LineTotal) AS 'Total'
FROM Production.Product AS P
JOIN Sales.SalesOrderDetail AS SD ON SD.ProductID = P.ProductID
JOIN Sales.SalesOrderHeader AS SH ON SH.SalesOrderID = SD.SalesOrderID
JOIN Sales.Customer AS C ON SH.CustomerID = C.CustomerID
WHERE C.StoreID = #storeid
GROUP BY P.ProductID, P.Name
);
GO
See this for reference http://msdn.microsoft.com/en-us/library/ms191165(v=sql.105).aspx
EDIT
Instead of using dynamic sql try giving a thought on
SELECT * FROM
FROM [dbo].[Person]
WHERE ([PersonID] = #PersonID
OR #AreaID IS NULL
)
AND (([Code] BETWEEN #Code AND CHAR(255))
OR #Code IS NULL
)
AND (([Name] BETWEEN #Name AND CHAR(255))
OR #Name IS NULL
)
AND (([Notes] BETWEEN #Notes AND CHAR(255))
OR #Notes IS NULL
)
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.