I am trying to write a query inside a stored procedure where I would like the comparison operator to be a variable.
For instance, here is my SQL query:
DECLARE #DueDate dateTime = '2019-11-29'
DECLARE #DueDateOperator varchar = '>'
SELECT *
FROM someTable
WHERE dueDate [#DueDateOperator] #DueDate --This doesn't work
Is it possible to put a comparison operator in a variable? If not, how can I write this query better such that I can parameterize the comparison operator to anything such as = or < or <= or >=?
Thanks in advance.
Your code looks like SQL Server. First, avoid declaring variables with no length. This is a bug waiting to happen:
DECLARE #DueDateOperator varchar = '>';
For fun, you can ponder what the result will be if you use '<>' or `!='. Always use a length:
DECLARE #DueDateOperator varchar(32) = '>';
Then, the safest way to handle this would be boolean expressions
SELECT *
FROM someTable
WHERE (DueDateOperator = '=' AND dueDate = #DueDate) OR
(DueDateOperator = '<>' AND dueDate <> #DueDate) OR
(DueDateOperator = '<' AND dueDate < #DueDate) OR
(DueDateOperator = '<=' AND dueDate <= #DueDate) OR
(DueDateOperator = '>' AND dueDate > #DueDate) OR
(DueDateOperator = '>=' AND dueDate >= #DueDate) ;
This prevents any form of SQL injection.
Finally, you can only replace constant values in a string using parameters. If you want to replace an operator (or keyword or function name or table name and so forth), you have to munge the query string. This is unsafe, because it can open the way to SQL injection attacks. But the method would be:
declare #DueDate dateTime = '2019-11-29';
declare #DueDateOperator varchar(32) = '>';
DECLARE #sql nvarchar(max) = '
select *
from sometable
where dueDate #DueDateOperator #DueDate';
set #sql = replace(#sql, '#DueDateOperator', #DueDateOperator);
exec sp_executesql #sql,
N'#DueDate datetime',
#DueDate #DueDate;
Although you cannot pass the operator in as a parameter in dynamic SQL, you can pass the constant value. This uses sp_executesql which allows the passing of parameters.
You can use dynamic SQL as below:
DECLARE #DueDate dateTime = '2019-11-29'
DECLARE #DueDateOperator varchar = '>'
DECLARE #Cmd NVARCHAR(4000) = 'SELECT * FROM someTable WHERE dueDate ' + #DueDateOperator + '''' + CONVERT( NVARCHAR(10), #DueDate , 23) + ''''
SELECT #Cmd
EXECUTE(#Cmd)
It is possible, try this:
Declare #DueDate dateTime = '2019-11-29'
Declare #DueDateOperator varchar = '>'
Declare #sql Nvarchar(MAX) = ''
Set #sql = 'Select * from someTable where dueDate ' + #DueDateOperator + ' '''+ convert(varchar, #DueDate, 23) + ''' '
EXECUTE (#sql )
--print ( #sql)
Related
The following is my query and I need to column value in select subquery instead i get column name
#Temp = temporary table
insert into #Temp([dateTime],Reading)
values (#startDate,(select top(1) #trendId from TABLENAME where deviceTimestamp >= #startDate and deviceTimestamp < #tempdt order by deviceTimestamp desc))
ALTER PROCEDURE ProcName
#trendId as nvarchar(max),
#startDate as datetime,
#endDate as datetime
AS
BEGIN
declare #stt varchar(200) = 'select deviceTimestamp,' + #trendId + ' '+'as reading
from TableName
where deviceTimestamp >= '+#startDate+'and deviceTimestamp < '+#endDate+'
order by deviceTimestamp desc'
exec(#stt)END
I get the error :
Conversion failed when converting date and/or time from character string.)
DECLARE #sqlCommand varchar(1000)
DECLARE #columnList varchar(75)
DECLARE #city varchar(75)
SET #columnList = 'AddressID, AddressLine1, City'
SET #city = '''London'''
SET #sqlCommand = 'SELECT ' + #columnList + ' FROM Person.Address WHERE City = ' + #city
EXEC (#sqlCommand)
Check this link out. Dynamic SQL seems like what you need you can pass tablename,table columns dynamicly to your Query. so with this you will be able to pass column name as parameter-variable. otherwise you just selecting plainText as record.
ALTER PROCEDURE Example
(#BranchCode AS INT,
#Department VARCHAR(8000),
#DateFrom AS DATETIME,
#DateTo AS DATETIME,
#TransactionNumber AS NVARCHAR(30)
-- #Delimiter char(1)
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX)
SET #Sql = 'Select
Mast.Branch_code, Mast.purerq_id as purerq_id,
Max(Mast.purreq_date) as purreq_date, Max(Mast.Dept_code) as Dept_code,
Max(Mast.purreq_desc) as purreq_desc,
Sum(Det.purreq_qty2) as purreq_qty2,
Det.Item_Code, Max(Mast.purreq_docecNo) as DocNo,
Sum(IsNull(Det.purreq_qty1,0)) as purreq_qty1,
IsNull(Det.purerq_TranNo,'') as purerq_TranNo,
IsNull(Max(Mast.purerq_adduserid),'') as Adduserid,
Max(Det.ItemPacking_Code) as ItemPacking_Code
From
tbl_purreqMaster Mast, tbl_purreq_detail1 Det
Where
Mast.Branch_code = Det.Branch_code And
Mast.purerq_id = Det.purerq_id And
Mast.purreq_date Between ' + #DateFrom + ' And ' + #DateTo + ' And
Mast.Dept_code IN (' + REPLACE( #Department, '''','') + ')
Mast.Branch_code = ' + CAST(#BranchCode AS VARCHAR) + ' AND
Mast.purreq_docecNo =' + #TransactionNumber + '
Group By
Mast.Branch_code, Mast.purerq_id, Det.Item_Code, Det.purerq_TranNo'
PRINT #Sql
--exec sp_executesql #Sql
END
GO
EXEC Example 1,'7,8','2017-10-01 00:00:00','2017-10-30 00:00:00','ALIA/PURQGEN/0000001'
This query shows this error
Msg 241, Level 16, State 1, Procedure Example, Line 18
Conversion failed when converting date and/or time from character string.
However I have tried replace and cast functions to resolve this but I found no solution
I would use the #params that go along with sp_executesql for the parameters that do not need to be concatenated to your executed string. I would also recommend some more processing on #Department to prevent sql injection.
ALTER PROCEDURE Example
(#BranchCode AS INT,
#Department VARCHAR(8000),
#DateFrom AS DATETIME,
#DateTo AS DATETIME,
#TransactionNumber AS NVARCHAR(30)
-- #Delimiter char(1)
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX), #Params nvarchar(8000);
set #Params = '#BranchCode INT, #DateFrom DATETIME, #DateTo DATETIME, #TransactionNumber NVARCHAR(30)'
SET #Sql = 'Select
Mast.Branch_code, Mast.purerq_id as purerq_id,
Max(Mast.purreq_date) as purreq_date, Max(Mast.Dept_code) as Dept_code,
Max(Mast.purreq_desc) as purreq_desc,
Sum(Det.purreq_qty2) as purreq_qty2,
Det.Item_Code, Max(Mast.purreq_docecNo) as DocNo,
Sum(IsNull(Det.purreq_qty1,0)) as purreq_qty1,
IsNull(Det.purerq_TranNo,'') as purerq_TranNo,
IsNull(Max(Mast.purerq_adduserid),'') as Adduserid,
Max(Det.ItemPacking_Code) as ItemPacking_Code
From tbl_purreqMaster Mast
inner join tbl_purreq_detail1 Det
on Mast.Branch_code = Det.Branch_code
and Mast.purerq_id = Det.purerq_id
Where Mast.purreq_date Between #DateFrom and #DateTo
and Mast.Dept_code IN (' + REPLACE( #Department, '''','') + ')
and Mast.Branch_code = #BranchCode
and Mast.purreq_docecNo = #TransactionNumber
Group By
Mast.Branch_code, Mast.purerq_id, Det.Item_Code, Det.purerq_TranNo'
PRINT #Sql
--exec sp_executesql #Sql, #Params, #BranchCode, #DateFrom, #DateTo, #TransactionNumber
END
The best approach in SQL Server 2008 would be using table valued parameters for #DepartmentId:
Table-Valued Parameters - msdn
User-Defined Table Types - msdn
SQL Server 2008 Table-Valued Parameters and C# Custom Iterators: A Match Made In Heaven! - Leonard Lobel
Table Value Parameter Use With C# - Jignesh Trivedi
Using Table-Valued Parameters in SQL Server and .NET - Erland Sommarskog
SQLZim's solution should work. But since he doesn't mention it, I will point out that the reason for the error is this line:
Mast.purreq_date Between ' + #DateFrom + ' And ' + #DateTo + ' And
You need to CAST/CONVERT your datetime variables to nvarchar, and surround them with single-quotes.
DECLARE #GET DATETIME
SET #GET= GETDATE()
DECLARE #Val VARCHAR(10)
SET #Val='wk'
--SELECT DATEADD(#Type,2,#GET)
SELECT DATEPART(wk,GETDATE()) -- WORKING
The above line works but when i pass it as a paramter it doesn't work.
SELECT DATEPART(#val,GETDATE()) -- NOT WORKING
The interval passed seems to be of other data type.
Make it as Dynamic Query
DECLARE #sql nvarchar(500)
DECLARE #Val VARCHAR(10)
SET #Val='week' --Quarter, Month
set #sql = 'SELECT DATEPART('+#val+',GETDATE())'
exec sp_executesql #sql
Or Alternatively you can use this
If #val = 'Week'
SELECT DATEPART(Week,GETDATE())
Else If #val = 'Month'
SELECT DATEPART(Month,GETDATE())
....
I am trying to execute a dynamic query in which I am concatenating a date but failed in doing
DECLARE #pStartDate datetime
DECLARE #pEndDate datetime
DECLARE #query nvarchar(MAX)
Dynamic query1
set #query = 'Select * from Table1 From tblEvent
Where (EventDate Between' + #pStartDate + ' and ' + #pEndDate +')'
Exec(#query)
Error
Conversion failed when converting date and/or time from character string.
Dynamic query2
set #query = 'Select * from Table1 From tblEvent
Where (EventDate Between' + cast(#pStartDate as varchar) + ' and ' + cast(#pEndDate as varchar) +')'
Exec(#query)
Error
Incorrect syntax near 1 [1 stands for whatever date I passed to #pStartDate]
Please suggest me how to do it.
Thanks.
The really proper way to do this would be to use a parametrized query and having sp_executeSql execute this:
DECLARE #pStartDate datetime
DECLARE #pEndDate datetime
DECLARE #query nvarchar(MAX)
SET #pStartDate = '20080301'
SET #pEndDate = '20080331'
-- if you're setting a NVARCHAR variable - **DO USE** the N'..' prefix!
SET #query = N'SELECT * FROM dbo.Table1
WHERE OrderDate BETWEEN #StartDate AND #EndDate'
-- execute the dynamic SQL, with a list of parameters, and their values
EXEC sp_executesql #query,
N'#StartDate DATETIME, #EndDate DATETIME',
#StartDate = #pStartDate, #EndDate = #pEndDate
In that case, there's no fiddling around with string concatenation and missing quotes and messy stuff like that - just a clear, properly parametrized query that isn't vulnerable to SQL injection attacks, and that performs much better since it's execution plan can be reused for subsequent executions.
Add single quote.
Because date or string specify in single quote like this '12-01-2014'.
set #query = 'Select * from Table1 From tblEvent
Where (EventDate Between''' + #pStartDate + ''' and ''' + #pEndDate +''')'
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
)