Related
I am trying to set the default value of #Day to the current day. However, I am getting a couple errors on the initial DATEADD(Day, DATEDIFF(Day, 0, GETDATE()), 0) and on all the #Days below it. I thought that the #Day DateTime under the alter procedure declared #Day but I am getting the error:
Must declare the scalar variable "#Day"
Anyone know what to do?
ALTER PROCEDURE [dbo].[InsertWeeklyRuns]
#Day DateTime = DATEADD(Day, DATEDIFF(Day, 0, GETDATE()), 0)
AS
BEGIN
SET NOCOUNT ON
DECLARE #Enddate DATETIME
DECLARE #StartDate DATETIME
DECLARE #F1Runs INT
DECLARE #F2Runs INT
DECLARE #F3Runs INT
DECLARE #F1Alarms INT
DECLARE #F2Alarms INT
DECLARE #F3Alarms INT
SET #Day = DATEADD(dd, DATEDIFF(dd, 0, #Day), 0)
SET #Enddate = CASE
WHEN #Day > DATEADD(Day, DATEDIFF(Day, 0, GETDATE()), 0)
THEN DATEADD(Day, DATEDIFF(Day, 0, GETDATE()), 0)
ELSE DATEADD(Day, 1, #Day)
END
SET #StartDate = #Enddate - 7
Thanks
This should work as you expected:
CREATE PROCEDURE [dbo].[InsertWeeklyRuns]
#Day DateTime
AS
BEGIN
SET NOCOUNT ON
Declare #Enddate Datetime
Declare #StartDate Datetime
Declare #F1Runs Int
Declare #F2Runs Int
Declare #F3Runs Int
Declare #F1Alarms Int
Declare #F2Alarms Int
Declare #F3Alarms Int
DECLARE #today date = CAST(GETDATE() AS date);
IF #Day IS NULL
Set #Day = #today
Set #Enddate = CASE WHEN #Day >= #today THEN #today ELSE DATEADD(Day, 1, #Day) END
Set #StartDate = #Enddate - 7
END
I am trying to write a stored procedure that will be called by an automated report on the 1st, 8th, 15th, and 22nd of each month.
Below I have the code I am currently trying to use however I keep getting the error Explicit conversion from data type int to date is not allowed. on the line for #StartDate if ran on the 1st. However I have tried commenting out that section and I just get the same error but then for the next #StartDate.
I need #StartDate and #EndDate to be able to change based on which day of the month the report is currently being ran.
On the 1st it would need to be ran from the 21st of the
previous month to the end of the previous month.
On the 8th it would need to be 1st of the current month to the 7th.
On the 15th it would need to be 8th of the current month to the 14th.
On the 22nd it would need to be 15th of the current month to the 21st.
Declare #RunDate date
Declare #StartDate Date
Declare #EndDate Date
Set #RunDate = '3/1/2017'
If (Day(#RunDate) = 1)
Begin
Set #StartDate = Convert(Date,(Month(DATEADD(d,-1,#Rundate)) + '/21/' + Case When Month(#Rundate) = '1' Then Year(#RunDate) Else Year(DATEADD(yy,-1,#Rundate)) End),101)
Set #EndDate = Convert(date,DATEADD(ms,-3,DATEADD(mm,0,DATEADD(mm,DATEDIFF(mm,0,#Rundate),0))),101)
End
Else
IF (Day(#RunDate) = 8)
Begin
Set #StartDate = Convert(Date,(Month(#Rundate) + '/1/' + Year(#RunDate)),101)
Set #EndDate = Convert(Date,(Month(#Rundate) + '/7/' + Year(#RunDate)),101)
End
Else
If (Day(#Rundate) = 15)
Begin
Set #StartDate = Convert(Date,(Month(#Rundate) + '/8/' + Year(#RunDate)),101)
Set #EndDate = Convert(Date,(Month(#Rundate) + '/14/' + Year(#RunDate)),101)
End
Else
If (Day(#Rundate) = 22)
Begin
Set #StartDate = Convert(Date,(Month(#Rundate) + '/15/' + Year(#RunDate)),101)
Set #EndDate = Convert(Date,(Month(#Rundate) + '/21/' + Year(#RunDate)),101)
End
I am using SQL Server 2012 back end with SSMS 2016 front end. If that helps.
You can simplify all of that down to:
Declare #RunDate date, #StartDate date, #EndDate date;
Set #RunDate = '20170301';
If (Day(#RunDate) = 1)
Begin;
Set #StartDate = dateadd(day,20,dateadd(month, datediff(month, 0, #RunDate)-1, 0))
Set #EndDate = dateadd(day,-1,dateadd(month, datediff(month, 0, #RunDate), 0))
End;
Else
If (Day(#RunDate) in (8,15,22))
Begin;
Set #StartDate = dateadd(day,Day(#RunDate)-8,dateadd(month, datediff(month, 0, #RunDate), 0))
Set #EndDate = dateadd(day,Day(#RunDate)-2,dateadd(month, datediff(month, 0, #RunDate), 0))
End;
Alternative using case, but doesn't verify for the day() in 8,15,22:
Declare #RunDate date, #StartDate date, #EndDate date;
Set #RunDate = '20170328';
set #StartDate = case day(#RunDate)
when 1 then dateadd(day,20,dateadd(month, datediff(month, 0, #RunDate)-1, 0))
else dateadd(day,Day(#RunDate)-8,dateadd(month, datediff(month, 0, #RunDate), 0))
end;
set #EndDate = case day(#RunDate)
when 1 then dateadd(day,-1,dateadd(month, datediff(month, 0, #RunDate), 0))
else dateadd(day,Day(#RunDate)-2,dateadd(month, datediff(month, 0, #RunDate), 0))
end;
rextester demo of both: http://rextester.com/CCUFI85069
Try changing the code to set the dates like this...
SET #StartDate = CONVERT(DATE,
CONCAT(MONTH(DATEADD(d, -1, #Rundate)),
'/21/',
IIF(MONTH(#Rundate) = '1', YEAR(#RunDate), YEAR(DATEADD(yy, -1, #Rundate)))) , 101)
The full code will look like this...
Declare #RunDate date
Declare #StartDate Date
Declare #EndDate Date
Set #RunDate = '3/1/2017'
If (Day(#RunDate) = 1)
Begin
Set #StartDate = CONVERT(DATE, CONCAT(MONTH(DATEADD(d, -1, #Rundate)), '/21/',IIF(MONTH(#Rundate) = '1', YEAR(#RunDate), YEAR(DATEADD(yy, -1, #Rundate)))) , 101)
Set #EndDate = Convert(date,DATEADD(ms,-3,DATEADD(mm,0,DATEADD(mm,DATEDIFF(mm,0,#Rundate),0))),101)
End
Else
IF (Day(#RunDate) = 8)
Begin
Set #StartDate = Convert(Date, CONCAT(Month(#Rundate) , '/1/' , Year(#RunDate)) ,101)
Set #EndDate = Convert(Date, CONCAT(Month(#Rundate) , '/7/' , Year(#RunDate)) ,101)
End
Else
If (Day(#Rundate) = 15)
Begin
Set #StartDate = Convert(Date, CONCAT(Month(#Rundate) , '/8/' , Year(#RunDate)) ,101)
Set #EndDate = Convert(Date, CONCAT(Month(#Rundate) , '/14/' , Year(#RunDate)) ,101)
End
Else
If (Day(#Rundate) = 22)
Begin
Set #StartDate = Convert(Date, CONCAT(Month(#Rundate) , '/15/' , Year(#RunDate)) ,101)
Set #EndDate = Convert(Date, CONCAT(Month(#Rundate) , '/21/' , Year(#RunDate)) ,101)
END
SELECT #StartDate, #EndDate
You could even hide the repetitive code and create a simple function...
CREATE FUNCTION [dbo].[GetDateBasedOnStringNumber] (#num NVARCHAR(20), #RunDate DATE)
RETURNS DATE
WITH SCHEMABINDING AS
BEGIN
DECLARE #retDate DATE
SELECT #retDate = Convert(Date, CONCAT(Month(#RunDate) , #num , Year(#RunDate)) ,101)
RETURN #retDate ;
END;
and then use this pattern instead...
SELECT #StartDate = dbo.GetDateBasedOnStringNumber('/1/', #RunDate)
SELECT #EndDate = dbo.GetDateBasedOnStringNumber('/7/', #RunDate)
The error is because you are trying to add integer and varchar values together. Using this line as an example:
Set #StartDate = Convert(Date,(Month(DATEADD(d,-1,#Rundate)) + '/21/' + Case When Month(#Rundate) = '1' Then Year(#RunDate) Else Year(DATEADD(yy,-1,#Rundate)) End),101)
If you break down the parts of your calculation you have the month integer value, a day varchar value and a year integer value. If you try to concatenate these you get the error message:
Msg 245, Level 16, State 1, Line 9 Conversion failed when converting
the varchar value '/21/' to data type int.
You could cast the integer values to varchar and it would work.
It is recommended to use the built-in date functions than using concatenation so you could achieve the same result like so:
DECLARE
#RunDate DATE = '20170101'
, #StartDate DATE
, #EndDate DATE
IF DATEPART(DAY, #RunDate) = 1
SELECT
#StartDate = DATEADD(DAY, -(DATEPART(DAY, DATEADD(DAY, -1, #RunDate)) - 20), #RunDate)
, #EndDate = DATEADD(DAY, -1, #RunDate)
SELECT
#StartDate
, #EndDate
I am working on a query something require DATE!!
DECLARE #YesterDay DATETIME, #Today DATETIME
SET #YesterDay = DateAdd(DD, DateDiff(DD, 0, GETDATE())-1, 0)
SET #Today = DateAdd(DD, DateDiff(DD, 0, GETDATE()), 0)
select #YesterDay = convert(varchar, getdate()-1 , 110)
select #Today = convert(varchar, getdate() , 110)
EXEC #return_value = [dbo].[post_sec_admin_list_user_log]
#pDateFr = #YesterDay ,
#pDateTo = #Today,
#pName = '',
#pSec = NULL
#DateFr is varchar(50)
#DateT0 is varchar(50)
the #dateFr and #dateTo are both varchar..
And I try to execute it, it print the time format as this 2011-06-09 16:15:38.927
error statement
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
Additionally, the varchar format I need is MM-DD-YYYY
Anyone know where is my error at?
thanks
Your code is confusing:
DECLARE #YesterDay DATETIME, #Today DATETIME
SET #YesterDay = DateAdd(DD, DateDiff(DD, 0, GETDATE())-1, 0)
SET #Today = DateAdd(DD, DateDiff(DD, 0, GETDATE()), 0)
select #YesterDay = convert(varchar, getdate()-1 , 110)
select #Today = convert(varchar, getdate() , 110)
So you declare it as DATETIME, set value with DateDiff then overwrite that value with an varchar representation of a date that is recalculated using different method.
At line 4 and 5 #Yesterday and #Today variables are still DATETIME, because it's declared that way.
EDIT: As mentioned in the comment to my answer you need a variable to pass to the procedure.
So the correct fix would be to declare the variables as VARCHAR(50) at the beginning and do the conversion directly.
DECLARE #YesterDay VARCHAR(50), #Today VARCHAR(50)
SELECT #YesterDay = convert(varchar(50), dateadd(dd, -1, getdate()) , 110)
SELECT #Today = convert(varchar(50), getdate() , 110)
And then call the procedure the way you did originally.
My guess is that getdate()-1 part is wrong.
Also, you were making dateadd and datediff why? Try it like this:
SET #YesterDay = dateadd(dd, -1, GETDATE())
SET #Today = GETDATE()
select #YesterDay = convert(varchar(50), dateadd(dd, -1, getdate()) , 110)
select #Today = convert(varchar(50), getdate() , 110)
EXEC #return_value = [dbo].[post_sec_admin_list_user_log]
#pDateFr = #YesterDay ,
#pDateTo = #Today,
#pName = '',
#pSec = NULL
I am using this query to get time taken.
SELECT DATEDIFF(dd, ActualStartDate, ActualCompletionDate) AS TimeTaken
FROM TableName
Now I want to exclude weekends and only include Mon-Fri as days counted.
Example query below, here are some details on how I solved it.
Using DATEDIFF(WK, ...) will give us the number of weeks between the 2 dates. SQL Server evaluates this as a difference between week numbers rather than based on the number of days. This is perfect, since we can use this to determine how many weekends passed between the dates.
So we can multiple that value by 2 to get the number of weekend days that occurred and subtract that from the DATEDIFF(dd, ...) to get the number of weekdays.
This doesn't behave 100% correctly when the start or end date falls on Sunday, though. So I added in some case logic at the end of the calculation to handle those instances.
You may also want to consider whether or not the DATEDIFF should be fully inclusive. e.g. Is the difference between 9/10 and 9/11 1 day or 2 days? If the latter, you'll want to add 1 to the final product.
declare #d1 datetime, #d2 datetime
select #d1 = '9/9/2011', #d2 = '9/18/2011'
select datediff(dd, #d1, #d2) - (datediff(wk, #d1, #d2) * 2) -
case when datepart(dw, #d1) = 1 then 1 else 0 end +
case when datepart(dw, #d2) = 1 then 1 else 0 end
I found when i used this there was a problem when d1 fell on saturday. Below is what i used to correct this.
declare #d1 datetime, #d2 datetime
select #d1 = '11/19/2011' , #d2 = '11/28/2011'
select datediff(dd, #d1, #d2) +case when datepart(dw, #d1) = 7 then 1 else 0 end - (datediff(wk, #d1, #d2) * 2) -
case when datepart(dw, #d1) = 1 then 1 else 0 end +
case when datepart(dw, #d2) = 1 then 1 else 0 end
BEGIN
DECLARE #totaldays INT;
DECLARE #weekenddays INT;
SET #totaldays = DATEDIFF(DAY, #startDate, #endDate)
SET #weekenddays = ((DATEDIFF(WEEK, #startDate, #endDate) * 2) + -- get the number of weekend days in between
CASE WHEN DATEPART(WEEKDAY, #startDate) = 1 THEN 1 ELSE 0 END + -- if selection was Sunday, won't add to weekends
CASE WHEN DATEPART(WEEKDAY, #endDate) = 6 THEN 1 ELSE 0 END) -- if selection was Saturday, won't add to weekends
Return (#totaldays - #weekenddays)
END
This is on SQL Server 2014
declare #d1 datetime, #d2 datetime
select #d1 = '4/19/2017', #d2 = '5/7/2017'
DECLARE #Counter int = datediff(DAY,#d1 ,#d2 )
DECLARE #C int = 0
DECLARE #SUM int = 0
WHILE #Counter > 0
begin
SET #SUM = #SUM + IIF(DATENAME(dw,
DATEADD(day,#c,#d1))IN('Sunday','Monday','Tuesday','Wednesday','Thursday')
,1,0)
SET #Counter = #Counter - 1
set #c = #c +1
end
select #Sum
If you hate CASE statements as much as I do, and want to be able to use the solution inline in your queries, just get the difference of days and subtract the count of weekend days and you'll have the desired result:
declare #d1 datetime, #d2 datetime, #days int
select #d1 = '2018/10/01', #d2 = '2018/11/01'
SET #days = DateDiff(dd, #d1, #d2) - DateDiff(ww, #d1, #d2)*2
print #days
(The only caveat--or at least point to keep in mind--is that this calculation is not inclusive of the last date, so you might need to add one day to the end date to achieve an inclusive result)
I just want to share the code I created that might help you.
DECLARE #MyCounter int = 0, #TempDate datetime, #EndDate datetime;
SET #TempDate = DATEADD(d,1,'2017-5-27')
SET #EndDate = '2017-6-3'
WHILE #TempDate <= #EndDate
BEGIN
IF DATENAME(DW,#TempDate) = 'Sunday' OR DATENAME(DW,#TempDate) = 'Saturday'
SET #MyCounter = #MyCounter
ELSE IF #TempDate not in ('2017-1-1', '2017-1-16', '2017-2-20', '2017-5-29', '2017-7-4', '2017-9-4', '2017-10-9', '2017-11-11', '2017-12-25')
SET #MyCounter = #MyCounter + 1
SET #TempDate = DATEADD(d,1,#TempDate)
CONTINUE
END
PRINT #MyCounter
PRINT #TempDate
If you do have a holiday table, you can also use that so that you don't have to list all the holidays in the ELSE IF section of the code. You can also create a function for this code and use the function whenever you need it in your query.
I hope this might help too.
Using https://stackoverflow.com/a/1804095 and JeffFisher30's answer above (https://stackoverflow.com/a/14572370/6147425) and my own need to have fractional days, I wrote this:
DateDiff(second,Start_Time,End_Time)/86400.0
-2*DateDiff(week, Start_Time, End_Time)
-Case When (DatePart(weekday, Start_Time)+##DateFirst)%7 = 1 Then 1 Else 0 End
+Case When (DatePart(weekday, End_Time)+##DateFirst)%7 = 1 Then 1 Else 0 End
Use this function to calculate the number of business days excluding Saturday and Sunday. Also it will exclude start date and it will include end date.
-- Select [dbo].[GetBussinessDays] ('02/18/2021', '03/06/2021') -- 11 days
CREATE or ALTER FUNCTION [dbo].[GetBussinessDays] (
#StartDate DATETIME,
#EndDate DATETIME
)
returns INT AS
BEGIN
DECLARE #tempStartDate DATETIME= #StartDate;
DECLARE #tempEndDate DATETIME = #EndDate;
IF(#tempStartDate IS NULL
OR
#tempEndDate IS NULL)
BEGIN
RETURN NULL;
END
--To avoid negative values reverse the date if StartDate is grater than EndDate
IF(#StartDate > #EndDate)
BEGIN
SET #StartDate = #tempEndDate;
SET #EndDate = #tempStartDate;
END
DECLARE #Counter INT = Datediff(day,#StartDate ,#EndDate);
DECLARE #TempCounter INT = 0;
DECLARE #TotalBusinessDays INT = 0;
WHILE #Counter >= 0
BEGIN
IF(#TempCounter > 0 OR #Counter = 1) -- To ignore first day's calculation
Begin
SET #TotalBusinessDays = #TotalBusinessDays + Iif(Datename(dw, Dateadd(day,#TempCounter,#StartDate)) IN('Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday'),1,0)
END
SET #Counter = #Counter - 1
SET #TempCounter = #TempCounter +1
END
RETURN #TotalBusinessDays;
END
Using #Derek Kromm answer (Current Marked Answer)
I have modified so it IS tolerant of any localisations that may be on the target server.
DECLARE #d1 DATETIME, #d2 DATETIME
SELECT #d1 = '10/01/2022', #d2 = '10/28/2022'
SELECT (datediff(dd, #d1, #EndQuery)+1) - (datediff(wk, #d1, dateadd(dd,1,#d2)) * 2)
- CASE WHEN DateName(WEEKDAY, #d1) = 'Sunday' THEN 1 ELSE 0 END -- This includes for start date edge case
+ CASE WHEN DateName(WEEKDAY, #d2) = 'Saturday' THEN 1 ELSE 0 END -- This includes for end date edge case.
This is with the end date being innclusive.
/*
EXAMPLE:
/MONDAY/
SET DATEFIRST 1
SELECT dbo.FUNC_GETDATEDIFFERENCE_WO_WEEKEND('2019-02-01','2019-02-12')
*/
CREATE FUNCTION FUNC_GETDATEDIFFERENCE_WO_WEEKEND
(
#pdtmaLastLoanPayDate DATETIME,
#pdtmaDisbursedDate DATETIME
)
RETURNS BIGINT
BEGIN
DECLARE
#mintDaysDifference BIGINT
SET #mintDaysDifference = 0
WHILE CONVERT(NCHAR(10),#pdtmaLastLoanPayDate,121) <= CONVERT(NCHAR(10),#pdtmaDisbursedDate,121)
BEGIN
IF DATEPART(WEEKDAY,#pdtmaLastLoanPayDate) NOT IN (6,7)
BEGIN
SET #mintDaysDifference = #mintDaysDifference + 1
END
SET #pdtmaLastLoanPayDate = DATEADD(DAY,1,#pdtmaLastLoanPayDate)
END
RETURN ISNULL(#mintDaysDifference,0)
END
In my table I have a Month(tinyint) and a Day(tinyint) field. I would like to have a function that takes this month and day and produces a datetime for the next date(including year) given this month and day.
So if I had Month = 9, Day = 7 it would produce 9/7/2009.
If I had Month 1, Day 1 it would produce 1/1/2010.
something like this would work. It's variation on your method, but it doesn't use the MM/DD/YYYY literal format, and it won't blowup against bad input (for better or for worse).
declare #month tinyint
declare #day tinyint
set #month = 9
set #day = 1
declare #date datetime
-- this could be inlined if desired
set #date = convert(char(4),year(getdate()))+'0101'
set #date = dateadd(month,#month-1,#date)
set #date = dateadd(day,#day-1,#date)
if #date <= getdate()-1
set #date = dateadd(year,1,#date)
select #date
Alternatively, to create a string in YYYYMMDD format:
set #date =
right('0000'+convert(char(4),year(getdate())),4)
+ right('00'+convert(char(2),#month),2)
+ right('00'+convert(char(2),#day),2)
Another method, which avoids literals all together:
declare #month tinyint
declare #day tinyint
set #month = 6
set #day = 24
declare #date datetime
declare #today datetime
-- get todays date, stripping out the hours and minutes
-- and save the value for later
set #date = floor(convert(float,getdate()))
set #today = #date
-- add the appropriate number of months and days
set #date = dateadd(month,#month-month(#date),#date)
set #date = dateadd(day,#day-day(#date),#date)
-- increment year by 1 if necessary
if #date < #today set #date = dateadd(year,1,#date)
select #date
Here is my sql example so far. I don't really like it though...
DECLARE #month tinyint,
#day tinyint,
#date datetime
SET #month = 1
SET #day = 1
-- SET DATE TO DATE WITH CURRENT YEAR
SET #date = CONVERT(datetime, CONVERT(varchar,#month) + '/' + CONVERT(varchar,#day) + '/' + CONVERT(varchar,YEAR(GETDATE())))
-- IF DATE IS BEFORE TODAY, ADD ANOTHER YEAR
IF (DATEDIFF(DAY, GETDATE(), #date) < 0)
BEGIN
SET #date = DATEADD(YEAR, 1, #date)
END
SELECT #date
Here's a solution with PostgreSQL
your_date_calculated = Year * 10000 + Month * 100 + Day
gives you a date like 20090623.
select cast( cast( your_date_calculated as varchar ) as date ) + 1
Here's my version. The core of it is just two lines, using the DATEADD function, and it doesn't require any conversion to/from strings, floats or anything else:
DECLARE #Month TINYINT
DECLARE #Day TINYINT
SET #Month = 9
SET #Day = 7
DECLARE #Result DATETIME
SET #Result =
DATEADD(month, ((YEAR(GETDATE()) - 1900) * 12) + #Month - 1, #Day - 1)
IF (#Result < GETDATE())
SET #Result = DATEADD(year, 1, #Result)
SELECT #Result