Using DATEDIFF to assign dynamic interval - sql-server

I have the following code, which works as expected.
DECLARE #startdate datetime2 = '2007-05-05';
DECLARE #enddate datetime2 = '2007-05-04';
SELECT DATEDIFF(year, #startdate, #enddate);
I now want to be able to use a variable to switch from "year, week, days, etc. So I tried this, but it didn't work. Any suggestions?
DECLARE #startdate datetime2 = '2007-05-05';
DECLARE #enddate datetime2 = '2007-05-04';
DECLARE #interval varchar = 'year'
SELECT DATEDIFF(#interval, #startdate, #enddate);
I get:
Msg 1023, Level 15, State 1, Line 25
Invalid parameter 1 specified for datediff.
Thank you,
Raul Gonzalez

You have to use dynamic sql for this. Once you start using dynamic sql you open yourself up to sql injection. Here is how you can do this and protect yourself against sql injection. The case expression may seem a little odd but if you have a value in your #interval that is not allowed the entire sql string will be NULL and no harm will come to your database. Also, please notice that you did not specify a length for #interval. For variables the default length is 1 so it would only be 'y' not 'year' as you expected it to be. Always specify the scale and precision of variables.
DECLARE #startdate datetime2 = '2007-05-05';
DECLARE #enddate datetime2 = '2007-05-04';
DECLARE #interval varchar(10) = 'year'
declare #SQL nvarchar(max)
set #SQL = 'select datediff(' +
case #interval when 'year' then 'year'
when 'week' then 'week'
when 'days' then 'day'
end
+ ', #startdate, #enddate)'
exec sp_executesql #SQL, N'#startdate datetime2, #enddate datetime2', #startdate, #enddate

You could do it dynamically:
declare #datediff varchar(max)
set #datediff = 'SELECT DATEDIFF(' + #interval + ',' + '''' + cast(#startdate as varchar(10)) + '''' + ',' + '''' + cast(#enddate as varchar(10)) + '''' + ')'
exec (#datediff)

Now let's say that I have the following:
DECLARE #startdate datetime2 = '2017-11-03';
DECLARE #enddate datetime2 = '2018-11-03';
DECLARE #interval varchar(10) = 'month'
declare #SQL nvarchar(max)
set #SQL = 'select datediff(' +
case #interval
when 'year' then 'year'
when 'month' then 'month'
when 'week' then 'week'
when 'day' then 'day'
end
+ ', #startdate, #enddate)'
exec sp_executesql #SQL, N'#startdate datetime2, #enddate datetime2',
#startdate, #enddate
The result is "12"
Now I'd like to have 12 values displayed as:
2017/11/03
2017/12/03
2018/01/03
2018/02/03
2018/03/03
2018/04/03
2018/05/03
2018/06/03
2018/07/03
2018/08/03
2018/09/03
2018/10/03

Related

Pivot table not worked by dynamic parameter

I wrote this query:
declare #startdate nvarchar(10) = '2016/01/01',
#enddate nvarchar(10) = '2019/10/04',
#cols nvarchar(max),
#strsql nvarchar(max);
select
#cols =
(select distinct QUOTENAME(maintenancename)+','
from (select distinct maintenancename
from Table_maintenancetype where coalesce(maintenancename, '') != '') AS t1
for xml path('')
)
set #cols=LEFT(#cols,LEN(#cols)-1)
set #strsql=N'select *
from
(select sum(timedurationok) as wt, maintenancetype,tcode
from Table_maintenancereport
where ((svok=1 and need_netok=0) or (svok=1 and netok=1 and need_netok=1))and tcode<>-1
and dateendrole >='+ #startdate+ 'and dateendrole<='+ #enddate+'
group by maintenancetype,tcode
) d
pivot
(sum(wt)
for maintenancetype in ('+#cols+')
) piv
'
exec sp_executesql #strsql
When execute this no record return but when replace #startdate and #enddate by '2016/01/01','2019/10/04'
The query works fine I don't know what the problem is... and when use between in query I get error for can not convert nvarchar to data int 2016
first, do a print #strsql to show your dynamic query and check.
You will see that your query is dateendrole >= 2016/01/01
the date should be in single quote dateendrole >= 2016/01/01'
your query should be
and dateendrole >= '''+ #startdate+ ''' and dateendrole<= '''+ #enddate+'''
a better way is to use parameter with sp_executesql
declare both the variable as date data type since it is going to hold date value
declare #startdate date = '20160101',
#enddate date = '20191004'
and in your dynamic query, just use the variable
and dateendrole >= #startdate and dateendrole <= #enddate
finally when calling sp_executesql, pass in the variable. And don't forget to print out the verify your query
print #strsql
exec sp_executesql #strsql, N'#startdate date, #enddate date', #startdate, #enddate

How to convert/cast column data type in concatenated SQL query string

i have the following variables defined in my stored procedure
#StartDate DateTime,
#EndDate DateTime,
I'm setting the sql to be executed dynamically, so when constructing the query where clause i have the below line.
SET #sql = #sql + ' AND (convert(datetime, R.ReportDate, 121) >= ' + #StartDate + 'AND convert(datetime, R.ReportDate, 121) <=' + #EndDate +')'
When i execute the stored procedure, the line above throws the error below
Conversion failed when converting datetime from character string.
If i change the variable datatype to NVARCHAR(MAX), the procedure executes successfully but then returns no rows because the date comparison/matching fails.
ReportDate column is of datatype datetime and has data in this format 2014-06-01 00:00:00.000
As you can see i have tried converting the column when constructing my query but that isn't working.
The problem is not with ReportDate, but when you are trying to concatenate your DateTime parameters with your nvarchar sql statement. The problem can be reproduced fairly simply with:
DECLARE #SQL NVARCHAR(MAX) = 'Some text' + GETDATE();
The wrong way to fix this is to convert the datetime parameter to a string so that it can be concatenated with a string, e.g.
SET #sql = #sql + ' AND r.ReportDate >= CONVERT(DATETIME, '''
+ CONVERT(VARCHAR(10), #StartDate, 112)
+ ''', 112) AND r.ReportDate <= CONVERT(DATETIME, ''' +
+ CONVERT(VARCHAR(10), #EndDate, 112)
+ ''', 112)';
N.B. I am including this for completeness of the answer, and in no way endorse this approach
The correct way to fix this is to use sp_executesql and pass properly typed parameters this way, this will avoid conversion issues. e.g.
SET #sql = #sql + 'AND r.ReportDate >= #StartDateParam AND r.ReportDate <= #EndDateParam';
EXECUTE sp_executesql
#sql,
N'#StartDateParam DATETIME, #EndDateParam DATETIME',
#StartDateParam = #StartDate,
#EndDateParam = #EndDate

Dates and Dynamic SQL error message

Using MS SQL 2012.
I am trying to query a set of tables using dynamic SQL and I am passing through the tablename, a start date in the format of dd/MM/yyyy and an end date in the format of dd/MM/yyyy.
My Code is as follows.
#tableName nvarchar(50),
#startDate date,
#endDate date
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Query varchar(max)
SET #Query = 'Select * from ''' + #tableName + ''' where Convert(date, Docdate, 103) >= ''' + CAST(#startDate AS VARCHAR(50)) + ''' and Convert(date, Docdate, 103) <= ''' + CAST(#endDate AS VARCHAR(50)) + ''
EXEC #Query
END
The Docdate field has a data type of Date and is in the format of yyyy-MM-dd.
I get the following error when I run this stored procedure.
Incorrect syntax near '/'.
What am I missing?
UPDATE
I am testing the query with the following entries for the variables and I still get the same error.
USE [TestDbs]
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[getResultSet]
#tableName = Prices,
#startDate = 01/02/2016,
#endDate = 20/02/2016
SELECT 'Return Value' = #return_value
GO
You should use sp_executesql and pass proper date parameters to your query, and avoid the need for casting dates to strings at all:
CREATE PROCEDURE dbo.YourProcedure
#TableName SYSNAME,
#StartDate DATE,
#EndDate DATE
AS
BEGIN
DECLARE #Query NVARCHAR(MAX)
SET #Query = 'SELECT * FROM ' + QUOTENAME(#TableName) +
' WHERE Docdate >= #StartDateParam
AND Docdate <= #EndDateParam';
EXECUTE sp_executesql
#Query,
N'#startDateParam DATE, #endDateParam DATE',
#StartDateParam = #StartDate,
#EndDateParam = #EndDate;
END
I have made a couple of other minor tweaks too:
Change the datatype of #TableName from NVARCHAR(50) to SYSNAME (Synonym for `NVARCHAR(128), the maximum length of an object name)
Change the data type of #Query to NVARCHAR(MAX) since this is the type sp_executesql expects.
Wrapped #TableName with QUOTENAME to ensure any special characters do not cause an error.
If DocDate is already a date, then the explicit convert is not necessary so I have removed this.
ADDENDUM
You may also wish to add some validation to the table name:
-- CHECK TABLE NAME IS A VALID TABLE OF VIEW
IF ISNULL(OBJECT_ID(#TableName, 'U'), OBJECT_ID(#TableName, 'V')) IS NULL
BEGIN
-- HANDLE INVALID NAME
RETURN;
END
EDIT
I have changed the procedure slightly to cater for sending through a schema qualified table name
CREATE PROCEDURE dbo.getResultSet
#TableName SYSNAME,
#StartDate DATE,
#EndDate DATE
AS
BEGIN
DECLARE #Query NVARCHAR(MAX)
SET #Query = 'SELECT * FROM ' +
QUOTENAME(OBJECT_SCHEMA_NAME(OBJECT_ID(#TableName))) + '.' +
QUOTENAME(OBJECT_NAME(OBJECT_ID(#TableName))) +
' WHERE Docdate >= #StartDateParam
AND Docdate <= #EndDateParam';
EXECUTE sp_executesql
#Query,
N'#startDateParam DATE, #endDateParam DATE',
#StartDateParam = #StartDate,
#EndDateParam = #EndDate;
END
Then you would use this to execute it:
SET DATEFORMAT DMY;
DECLARE #return_value int
EXEC #return_value = [dbo].[getResultSet]
#tableName = 'dbo.Prices',
#startDate = '01/02/2016',
#endDate = '20/02/2016'
SELECT 'Return Value' = #return_value;
Or better still use a culture insensitive date format (yyyyMMdd) for your literals:
DECLARE #return_value int
EXEC #return_value = [dbo].[getResultSet]
#tableName = 'dbo.Prices',
#startDate = '20160201',
#endDate = '20160220'
SELECT 'Return Value' = #return_value;

Error in Customized Procedure "Must declare the scalar variable #startdate"

I have a procedure to fetch data as per the criteria passed. I works fine with #month, #year, #quater.
ALTER PROCEDURE dbo.SPReportTimeSpan
(
#Week int = null,
#Month int = null,
#Year int = null,
#Quater int = null
)
AS
SET NOCOUNT ON
DECLARE #sql nvarchar(4000)
If (#Week) IS NOT NULL
BEGIN
DECLARE #startdate Date, #enddate Date
EXEC dbo.SPReturnStartEndDateOfSpecifiedWeek #Week, #startdate OUTPUT, #enddate OUTPUT
SELECT #startdate, #enddate
END
SELECT #sql='SELECT
CRMDR.Id as Id,.
CRMDR.Request_For_Id as Request_For_Id
From [CRM].[dbo].[CRM_Doctor_Request] AS CRMDR
WHERE CRMDR.Is_Deleted=0 '
If (#Month) IS NOT NULL
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) like (#Month) '
If (#Year) IS NOT NULL
SELECT #sql=#sql + ' AND YEAR(CRMDR.Date_Created) like (#Year) '
If (#Week) IS NOT NULL
SELECT #sql=#sql + ' AND (CRMDR.Date_Created) BETWEEN (#startdate) AND (#enddate) '
If (#Quater) IS NOT NULL
IF (#Quater = 1)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (4,5,6) '
IF (#Quater = 2)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (7,8,9) '
IF (#Quater = 3)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (10,11,12) '
IF (#Quater = 4)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (1,2,3) '
SELECT #sql=#sql + ' ORDER BY CRMDR.Id DESC '
EXEC sp_executesql #sql, N'#Month int, #Year int, #Quater int',
#Month, #Year, #Quater
RETURN
But for #week it gives error.
Running [dbo].[SPReportTimeSpan] ( #Week = 3, #Month = <NULL>, #Year = <NULL>, #Quater = <NULL> ).
Must declare the scalar variable "#startdate".
Column1 Column2
------------------------------ ------------------------------
2013-02-10 00:00:00.0000000 2013-02-17 00:00:00.0000000
No rows affected.
(1 row(s) returned)
#RETURN_VALUE = 0
Finished running [dbo].[SPReportTimeSpan].
I am not getting why its not working with week criteria. Please help if anyone have idea.
#startdate and #enddate are only accessible within the scope of your IF statement:
If (#Week) IS NOT NULL
BEGIN
DECLARE #startdate Date, #enddate Date
EXEC dbo.SPReturnStartEndDateOfSpecifiedWeek #Week, #startdate OUTPUT, #enddate OUTPUT
SELECT #startdate, #enddate
END
Move the declaration outside of the IF statement if you wish to reference these variables elsewhere:
DECLARE #startdate Date, #enddate Date
If (#Week) IS NOT NULL
BEGIN
EXEC dbo.SPReturnStartEndDateOfSpecifiedWeek #Week, #startdate OUTPUT, #enddate OUTPUT
SELECT #startdate, #enddate
END
FYI: The area where you make reference to these variables outside of the IF statement is here:
If (#Week) IS NOT NULL
SELECT #sql=#sql + ' AND (CRMDR.Date_Created) BETWEEN (#startdate) AND (#enddate) '
I got solution of my problem. To solve the problem, I need to explicitly cast datetime values to a character type so the query string can be concatenated as expected.
If (#Week) IS NOT NULL
SELECT #sql=#sql + ' AND (CRMDR.Date_Created) BETWEEN ''' + convert(VARCHAR,#startdate) + ''' AND ''' +convert(VARCHAR,#enddate) + ''''
or this (More Optimized)
SELECT #sql=#sql + ' AND (CRMDR.Date_Created BETWEEN #startdate AND #enddate)'
which needs to add something to sp_executesql
EXEC sp_executesql #sql, N'#Month int, #Year int, #Quater int, #Week int, #startdate datetime, #enddate datetime ',
#Month, #Year, #Quater, #Week, #startdate, #enddate
Your variables are out of scope.

Working with datetime with dynamic query in SQL Server

I am using a dynamic query wherein I want to use the variable which holds the datetime, whenever I execute the query it says cannot convert datetime from string, when I cast that variable to varchar(max), it takes it as string and not datetime, so how should I execute the query..
Below is my SQL query which I am trying to execute.
SET #SQL1 = 'SELECT B.FacId, B.FacName, B.BookCode, B.BookName, B.Quantity,
CONVERT(VARCHAR(10), B.TillDate, 104) AS TILLDATE FROM '+#TABLE+' B
WHERE B.TillDate BETWEEN CONVERT(VARCHAR(10),'+#FROMDATE+', 101) and
CONVERT(VARCHAR(10), DATEADD(DD,1,'+#TODATE+'), 101)'
EXEC SP_EXECUTESQL #SQL1
here #fromdate and #todate are the datetime type coming from different temp table. and stored in these variable..
How should I execute this query?
You need to quote your dates..
SET #SQL1 =
'SELECT B.FacId,
B.FacName,
B.BookCode,
B.BookName,
B.Quantity,
CONVERT(VARCHAR(10), B.TillDate, 104) AS TILLDATE
FROM '+#TABLE+' B
WHERE B.TillDate BETWEEN ''' + CONVERT(VARCHAR(10),#FROMDATE, 101) + ''' and ''' + CONVERT(VARCHAR(10),DATEADD(DD,1,#TODATE), 101) + ''''
You should not concatenate your parameter values like this. The best solution is to use a parameterized query with sp_executesql.
DECLARE #sql nvarchar(4000)
select #sql = N'
SELECT B.FacId
, B.FacName
, B.BookCode
, B.BookName
, B.Quantity
, CONVERT(VARCHAR(10), B.TillDate, 104) AS TILLDATE
FROM ' + quotename(#TABLE) + N' B
WHERE B.TillDate BETWEEN cast(floor(cast(#fromDate as float)) as datetime)
AND cast(floor(cast(#toDate as float)) as datetime)'
EXEC sp_executesql #sql, N'#fromDate datetime, #toDate datetime', #FROMDATE, #TODATE
Things to note about sp_executesql are:
The parameters are NVARCHAR values
The 3rd and 4th parameter keep their original datatype and do not need to be converted to a varchar. This again protects agains SQL Injection and it makes the query more readable as you prevent the quote soup which is so common in Dynamic SQL
Some additional changes were applied to the query:
The table name is wrapped in the QUOTENAME() function which protects against sql injection on the object name
The way the date part of the datetime variables is removed is not very optimal. Doing a convert(,,101) is an expensive operation which can better be done using the casting to float and taking floor of that value.
I'd like to see your variable definitions but I suspect it's because #FROMDATE and #TODATE are datetime and you're using them in a string concatenation statement. Thus you can fix it by:
SET #SQL1 = 'SELECT B.FacId, B.FacName, B.BookCode, B.BookName, B.Quantity, CONVERT(VARCHAR(10), B.TillDate, 104) AS TILLDATE FROM '+#TABLE+' B WHERE B.TillDate BETWEEN CONVERT(VARCHAR(10),'+CAST(#FROMDATE as varchar(15))+', 101) and CONVERT(VARCHAR(10), DATEADD(DD,1,'+CAST(#TODATE as varchar(15))+'), 101)'
However better solutions are:
Don't use dynamic SQL at all, maybe #TABLE doesn't vary that much and you can union them in to a view or something
Pass the parameters directly in to sp_executeSQL and thus preserve their types e.g.
SET #SQL1 = 'SELECT B.FacId, B.FacName, B.BookCode, B.BookName, B.Quantity, B.TillDate AS TILLDATE FROM '+#TABLE+' B
WHERE B.TillDate BETWEEN #inFROMDATE and #inTODATE'
EXEC SP_EXECUTESQL #SQL1,'#inFROMDATE datetime, #inTODATE',#inFromDate = #FROMDATE, #inTODATE = #TODate
I think this might work:
DECLARE #tempdate datetime
SET tempdate =DATEADD(DD,1,#TODATE)
SET #SQL1 = 'SELECT B.FacId, B.FacName, B.BookCode, B.BookName, B.Quantity,'''+ cast (B.TillDate as VARCHAR(50))+''' AS TILLDATE FROM '+#TABLE+' B WHERE B.TillDate BETWEEN '''+cast(#FROMDATE as VARCHAR(50))+''' and '''+cast(#tempdate as VARCHAR(50))'''
EXEC SP_EXECUTESQL #SQL1
Try this:
declare #sql1 varchar(max)
declare #table sysname
declare #FROMDATE datetime
declare #TODATE datetime
set #table = 'MyTable'
set #FROMDATE = GETDATE()
set #ToDATE = GETDATE()
SET #SQL1 = 'SELECT B.FacId, B.FacName, B.BookCode, B.BookName, B.Quantity,
CONVERT(VARCHAR(10), B.TillDate, 104) AS TILLDATE FROM '+#TABLE+' B
WHERE B.TillDate BETWEEN CONVERT(Datetime,''' + CONVERT(VARCHAR(10),#FROMDATE, 101)
+ ''', 101) and CONVERT(DATETIME,'''+ CONVERT(VARCHAR(10), DATEADD(DD,1,#TODATE), 101) + ''', 101)'
print #sql1
But look at Joel Mansford's answer to avoid the double conversion.
This is late, but may be it help someone
What you need is a quotes around your date,
You already got your answer.
Below is an example of what I usually put in my query
'(CONVERT(DATETIME,CONVERT(varchar,gd.CreatedDate),106) <= CONVERT(DATETIME,'''+CONVERT(varchar, #EndDate ) +''',106))'
note that #EndDate is of type Datetime here
Run this example and adapt it to your code.
(There are not 3 contiguous single quotes)
Declare #FromDATE datetime
;Declare #ToDATE datetime
;set #FromDATE = getdate()
;set #ToDATE = #FromDATE
;Print 'WHERE TillDate BETWEEN ' + char(39) + CONVERT(VARCHAR(10),#FromDATE, 101)
+ char(39) + ' and ' + char(39) + CONVERT(VARCHAR(10),#ToDATE, 101) + char(39)
I needed to pass a date as a variable to a stored procedure I was executing within the dynamic SQL:
Declare #asOfDate date = '2020-01-01'
Declare #sql nvarchar(max)
Set #sql='SELECT * FROM OPENROWSET(''SQLNCLI''
,''Server=localhost;Database=YOURDB;Uid=DBO_DB;Pwd=password''
,''SET FMTONLY OFF;SET NOCOUNT ON;
declare #date date ;
select #date = CONVERT(DATE,CONVERT(varchar(10),'+ CONVERT(VARCHAR(100),#asOfDate , 112)+'));
exec your_proc_here #date
'')'
Print #sql
--Exec(#sql)

Resources