Get dates in range - sql

I have a start date and an end date. I want to use datepart to check if any of the dates in between fall on weekends. How do I get the dates in between? Since they are not in a field I can't select them.

For this a calendar table would come in handy, but unless you have one already you can generate a series of the dates in between your start and end dates using a number sequence and test the weekday of each date in the series like below.
SET DATEFIRST 1 --set the start of the week to Monday.
DECLARE #startdate date = '2014-10-01', #enddate date = '2014-10-31'
SELECT DATEADD(DAY,number,#startdate)
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number,#startdate) <= #enddate
AND DATEPART(DW,DATEADD(DAY,number,#startdate)) IN (6,7)
This code will list all Saturdays and Sundays in October and the result will be:
2014-10-04
2014-10-05
2014-10-11
2014-10-12
2014-10-18
2014-10-19
2014-10-25
2014-10-26

Related

How can I get the last day of periodic months in SQL?

I am preparing the next 5 months date according to the value given in the query I wrote.
DECLARE #StartDate DATETIME = '2022-03-31', #monthadd INT = 5;
; WITH dates AS (
SELECT #StartDate [vade]
UNION ALL
SELECT DATEADD(MONTH,1,[vade])
FROM dates
WHERE DATEADD(MONTH,1,[vade]) <= DATEADD(MONTH,#monthadd,#StartDate)
)
SELECT *
FROM dates
OPTION (MAXRECURSION 0)
GO
However, when the last day of the month is 31, it is necessary to list the last day, which is the nearest day, in the following months. how do i do this?
Actual results
vade
2022-03-31 00:00:00.000
2022-04-30 00:00:00.000
2022-05-30 00:00:00.000
2022-06-30 00:00:00.000
2022-07-30 00:00:00.000
2022-08-30 00:00:00.000
Edit:
This is a maturity plan. If the person makes installments on the 31st of the month, the payment must be made on the last day of each month. If he does it on the 30th, the month should have 30 if it has 30 days, 30 if it has 31 days, and 29 if it has 29 days. If maturity starts on the 20th, it must be the 20th of each month. Imagine you take out a loan on the 30th of the month. If the month is 29 days, they will ask you to pay on the 29th day, and if the month is 31 days, they will ask you to pay on the 30th day. I know it's very confusing and I'm sorry about that.
Updated 2022-04-01
If I'm understanding correctly, you want to return the same "day" for each month - except when #StartDate is the last day of the month.
One approach would be to determine if the #StartDate is the last day of the month. If so, use EOMONTH() to return the last day in each of the subsequent months. Otherwise, use DATEADD() to return the specified "day" in each month. This approach should work for any date.
One approach is as follows:
If Maturity Date is last day of month, OR Maturity Day of month is > number of days in subsequent month, use EOMONTH() to return the last day of that month
Otherwise, use DATEADD() and DATEFROMPARTS() to generate the next date using the Maturity Day of month
SQL:
-- Note: Using 12 months for demo only
; WITH dates AS (
SELECT #StartDate AS MaturityDate
, IIF(#StartDate = EOMONTH(#StartDate), 1, 0) AS IsEOM
UNION ALL
SELECT
CASE -- Maturity date is last day of month OR
-- Maturity "day" is > number of days in current month
WHEN IsEOM = 1 OR DAY(#StartDate) > DAY( EOMONTH(NextMaturityDate) )
THEN EOMONTH( DATEADD(MONTH, 1, MaturityDate ))
-- Otherwise, maturity "day" is valid for current month
ELSE DATEFROMPARTS(
Year(NextMaturityDate)
, Month(NextMaturityDate)
, DAY(#StartDate)
)
END
, IsEOM
FROM ( SELECT MaturityDate
, IsEOM
, DATEADD(MONTH, 1, MaturityDate) AS NextMaturityDate
FROM dates
) t
WHERE MaturityDate < #EndDate
)
SELECT MaturityDate AS [vade]
FROM dates
OPTION (MAXRECURSION 0)
Results for 2022-03-31 (Last Day Of Month)
vade
2022-03-31
2022-04-30
2022-05-31
2022-06-30
2022-07-31
2022-08-31
2022-09-30
2022-10-31
2022-11-30
2022-12-31
2023-01-31
2023-02-28
2023-03-31
2023-04-30
2023-05-31
2023-06-30
Results for 2022-03-30 (NOT Last Day Of Month)
vade
2022-03-30
2022-04-30
2022-05-30
2022-06-30
2022-07-30
2022-08-30
2022-09-30
2022-10-30
2022-11-30
2022-12-30
2023-01-30
2023-02-28
2023-03-30
2023-04-30
2023-05-30
2023-06-30
db<>fiddle here
The DATEADD function already takes into account of the corner cases, like the end of the month, so you don't need to handle it.
In order to have a cleaner code, you can lay down a stored procedure, that creates (or replaces) a dates_list table and then cycles over the number of months to add to the start date.
DELIMITER //
CREATE OR REPLACE PROCEDURE create_dates_list (
IN start_date DATETIME,
IN num_months INT
)
BEGIN
DECLARE idx INT DEFAULT 0;
CREATE OR REPLACE TABLE dates_list (
date DATE
);
WHILE idx <> num_months DO
INSERT INTO tab VALUES(
DATEADD(#start_date, INTERVAL #idx MONTH)
);
SET idx = idx + 1;
END WHILE;
END //
DELIMITER ;
When you need to obtain new dates, you can refresh that table by setting the parameters and calling the stored procedure:
DECLARE #StartDate DATETIME = '2022-03-31', #monthadd INT = 5;
CALL create_dates_list(#StartDate, #monthadd);
You can freely access the table anytime by using the tools that sql empowers you with.
If you don't need the table to exist for further sessions, you can define the table as TEMPORARY. The official documentation on temporary tables is very detailed and comprehensive of examples, check it out to get to know more about it.

How to get a specific time window in SQL for the past 12 Months

I am looking for a way to get a certain time window of the past 12 Months from current date.
I want to get every day from the past 12 Months but there is a specific time window in between those dates because there is a start date and an end date.
So one day would be.
SELECT startdate, enddate
FROM TableX
WHERE
startdate >= SMALLDATETIMEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), DAY(GETDATE()-1), 05, 00)
AND enddate <= SMALLDATETIMEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), DAY(GETDATE()), 05, 00)
That would give me yesterdays date "2021-11-14T05:00:00Z" to todays date ("2021-11-15T05:00:00Z") .
Now I want this for the past 12 Months. Between yesterdays date an todays date are different timestamps that come with a specific value.
So I need to sum up every value in beetween that timestamp.
Using the EOMONTH function you can also calculate the start of the month for a given date.
So this
select
CAST(DATEADD(MONTH,-12,DATEADD(DAY,1,EOMONTH(GETDATE(),-1))) AS SMALLDATETIME) AS startDate,
CAST(DATEADD(DAY,1,EOMONTH(GETDATE(),-1)) AS SMALLDATETIME) AS endDate
Returns this (when run today, November 2021)
startDate
endDate
2020-11-01 00:00
2021-11-01 00:00
The query for the previous 12 months :
SELECT startdate, enddate
FROM TableX
WHERE startdate >= CAST(DATEADD(MONTH,-12,DATEADD(DAY,1,EOMONTH(GETDATE(),-1))) AS SMALLDATETIME)
AND enddate < CAST(DATEADD(DAY,1,EOMONTH(GETDATE(),-1)) AS SMALLDATETIME)

How to return the correct start date value from a stored procedure in this case?

I am using below case condition to calculate a StartDate variable:
DECLARE #StartDate Date =
DATEADD(DAY, CASE WHEN DATEPART(dw, GETDATE()) = 2 THEN -10 ELSE -8 END, GETDATE())
AS you can see, if today's date is Monday then 10 days are subtracted from today's date, else 8 days are deducted. Since today (10/16/2018) is not a Monday, this would return 10/8/2018 as an output (8 days deduction). If executed on Monday (10/15) then #StartDate would return 10/5 as output.
I added a table called Holidays and it has two columns Holiday Name and Date containing three records for this year: Columbus Day: 10/8, Veteran day 11/12 and Thanksgiving 11/22.
Now the challenging part I have to do is, if the returned output of #StartDate is 10/8 (query executed today: 10/16) which is a holiday (Monday), then value of #StartDate should be changed to 10/5 (previous business day, Saturday & Sunday excluded). Also if the value of #StartDate is 10/5 (query executed on 10/15), then the value should be changed to 10/4 since 10/8 was a holiday, it won't be counted for deduction of 10 days so instead of 10/5, it should be 10/4.
So in theory it should work as: check if there are any holidays that fall between today's date and #StartDate, and if so then use prior day and adjust for Mondays accordingly based on the scenario I mentioned above.
Please note that the included statement I mentioned at the top is part of a stored procedure.
How can I make this work, can someone help? Thanks in advance.
Assuming that there are never multiple dates in a row in your holiday, this will do it in a single(ish) statement. You could easily break this into two if you prefer:
DECLARE #StartDate date
;with d as (
select convert(date,dateadd(day,case when datepart(dw,getdate())=2 then -10 else -8 end,getdate())) dt
)
select #StartDate=case when holiday.date is null then dt else dateadd(day,-1,dt) end
from d
left join holiday on holiday.date=dt
EDIT: This is a complete working copy. I create a table variable to hold holidays, run the initial query, then insert the returned date as a holiday, then run the query again. If you run this in sql server you will see that the second query returns the day before the first query.
declare #holiday table(date date)
DECLARE #StartDate date
;with d as (
select convert(date,dateadd(day,case when datepart(dw,getdate())=2 then -10 else -8 end,getdate())) dt
)
select #StartDate=case when holiday.date is null then dt else dateadd(day,-1,dt) end
from d
left join #holiday holiday on holiday.date=dt
select #startdate
insert #holiday values (#startdate)
;with d as (
select convert(date,dateadd(day,case when datepart(dw,getdate())=2 then -10 else -8 end,getdate())) dt
)
select #StartDate=case when holiday.date is null then dt else dateadd(day,-1,dt) end
from d
left join #holiday holiday on holiday.date=dt
select #startdate

Dynamically calculate end of fiscal year(s) and weeks to that date

I'm looking to calculate how many weeks an employee will have worked if they started mid year until the end of the current fiscal year (1st April - 31st March).
For example, an employee started working at the company on 01/10/2017 (UK date) I need to calculate the number of weeks they will have worked until 31/03/2018 (inclusive).
The field for the employee start date is 'START_DATE' from table 'Employee'. I also have a calendar table with every date format you could imagine and also includes fiscal year.
I found this question but it doesn't quite solve my problem: Calculate totals of field based on current fiscal year only - SQL
Any help much appreciated.
It does depend on how you classify what a week is. Does it have to be a full week? Does starting on a day that is not a Monday mean that it's not counted as a full week if they finish on a day that is not Friday? This is where you have to identify your business logic.
Here are some fundamental DATEDIFF operations that you can use to work out differences between two dates, which you can use as a basis for your calculations:
DECLARE #startDate DATE = '2017-10-01'
DECLARE #endDate DATE = '2018-03-31'
SELECT #startDate StartDate, #endDate EndDate,
DATEDIFF(DAY, #startDate, #endDate) DaysPassed,
DATEDIFF(WEEK, #startDate, #endDate) WeeksWorked,
DATEDIFF(DAY, #startDate, #endDate) / 7.0 CalculatedWeeksWorked
Produces:
StartDate EndDate DaysPassed WeeksWorked CalculatedWeeksWorked
---------- ---------- ----------- ----------- ---------------------
2017-10-01 2018-03-31 181 25 25.857142
Also, you may want to consider the number of days worked excluding weekends to work out how many full weeks are worked, if so, have a look at this post:
get DATEDIFF excluding weekends using sql server
Fiscal Year
To work out the fiscal year, you should be able to simply look at the month value of the date like so:
DECLARE #startDate DATE = '2017-10-01';
-- if the month is greater than 3, add a year, else take the current year
SELECT CASE
WHEN DATEPART(MONTH, #startDate) > 3 THEN
CAST(DATEPART(YEAR, #startDate) + 1 AS VARCHAR(10)) + '-03-31'
ELSE
CAST(DATEPART(YEAR, #startDate) AS VARCHAR(10)) + '-03-31'
END AS fiscalYearEnd;
Edit the #startDate and test the above, it should hopefully work for most cases. I've given it a quick test and it seems to return the expected result.
DATEDIFF function:
ROUND(DATEDIFF(CURRENT_TIMESTAMP, START_DATE)/7, 0) AS weeks
where 0 is the number of decimal.
The problem with WEEKS is that it won't return correct results for dates that cross over January 1st.

Getting the date for each day within a date range in SQL

I'm looking for a way to get a list of instances a certain day appears between two date periods in SQL.
I have a range:
DECLARE #ViewStartDate DATETIME
DECLARE #ViewEndDate DATETIME
SET #ViewStartDate = '2014-09-08 00:00:00.000';
SET #ViewEndDate = '2014-09-30 00:00:00.000';
And need to get e.g. every Monday (date) within that list. I have tried looking all over for an answer as specific as this and can't seem to find anything relevant.
The reason is that it will be used in a logistics program to calculate delivery dates between a date range where the required delivery day is every Monday.
You can use the following query.
The first part in the cte is to find the next monday after the startdate. Then keep adding one week until the end date.
;WITH Dates([Date])
AS
(
--If the start date is sunday or monday
SELECT CASE WHEN DATEPART(WEEKDAY,#ViewStartDate) <=2
--Then the monday of that week
THEN DATEADD(WEEK, DATEDIFF(WEEK, 1, #ViewStartDate), 0)
ELSE
-- Mondy of the next week
DATEADD(WEEK,1,DATEADD(WEEK, DATEDIFF(WEEK, 0, #ViewStartDate), 0))
END
UNION ALL
-- Loop by joining the CTE Dates, until the date < #ViewEndDate
SELECT DATEADD(WEEK, 1,Date)
FROM Dates
WHERE DATEADD(WEEK, 1,Date) <= #ViewEndDate
)
SELECT [Date]
FROM Dates;