SQL- How to split two dates into yearly format - sql

If I have two dates for example:
#StartDate = '2009/01/01'
#EndDate = '2015/02/05'
is there a way for it to display in this format:
StartDate EndDate
2009/01/01 2010/01/01
2010/01/01 2011/01/01
2011/01/01 2012/01/01
2012/01/01 2013/01/01
2013/01/01 2014/01/01
2014/01/01 2015/01/01
2015/01/01 2015/02/05 <--- most importantly end using the end date
is this possible? because I have seen a number of CTE code snippets but they only split upto 01/01/2015 and not continue to the end of period 05/02/2015? Is there any possible chance for it include the remainder of the final period - 2015/01/01 - 2015/02/05 even though its not a year?

Here is a way:
with nums as (
select 0 as n
union all
select 1 + n
from nums
where n < 100
)
select DATEADD(year, n, #StartDate) as StartDate,
(case when DATEADD(year, n+1, #StartDate) >= #EndDate then #EndDate
else DATEADD(year, n+1, #StartDate)
end) as EndDate
from nums
where dateadd(year, nums.n, #StartDate) < #EndDate
If your periods are really long, you might need to expand nums beyond 100 values.

DECLARE #StartDate DATETIME = '2009/01/01'
DECLARE #EndDate DATETIME = '2015/02/05'
;WITH Dates AS
(
SELECT
StartDate = #StartDate,
EndDate = DATEADD(YEAR, 1, #StartDate)
UNION ALL
SELECT
StartDate = DATEADD(YEAR, 1, StartDate),
EndDate = CASE
WHEN DATEADD(YEAR, 1, EndDate) > #EndDate
AND DATEADD(YEAR, -1, EndDate) < DATEADD(YEAR, -1, #EndDate) THEN #EndDate
ELSE DATEADD(YEAR, 1, EndDate)
END
FROM Dates
WHERE EndDate < #EndDate
)
SELECT * FROM dates
OPTION (MAXRECURSION 0)
demo

Related

How to get date of a beginning of first work weekday even if the Monday is from last month?

My goal to is get get query that will return weekdays in a month. I can get the days of the month but I need to get dates starting from monday through Friday even if the Monday may be in the preceding month.
Example April 1st is a wednesday so I would need to bring back March 30th and 31st. And the last date returned would be by May 1st as that is the last friday that contains some April days..
If interested in a helper function, I have TVF which generates a calendar.
Example
Select * from [dbo].[tvf-Date-Calendar-Wide]('2020-04-01')
Returns
So, with a little tweak, we get can
Select WeekNr = RowNr
,B.*
From [dbo].[tvf-Date-Calendar-Wide]('2020-04-01') A
Cross Apply ( values (Mon)
,(Tue)
,(Wed)
,(Thu)
,(Fri)
) B(Date)
Which Returns
WeekNr Date
1 2020-03-30
1 2020-03-31
1 2020-04-01
1 2020-04-02
1 2020-04-03
2 2020-04-06
2 2020-04-07
2 2020-04-08
...
5 2020-04-29
5 2020-04-30
5 2020-05-01
The Function If Interested
CREATE FUNCTION [dbo].[tvf-Date-Calendar-Wide] (#Date1 Date)
Returns Table
Return (
Select RowNr,[Sun],[Mon],[Tue],[Wed],[Thu],[Fri],[Sat]
From (
Select D
,DOW=left(datename(WEEKDAY,d),3)
,RowNr = sum(Flg) over (order by D)
From (
Select D,Flg=case when datename(WEEKDAY,d)= 'Sunday' then 1 else 0 end
From (Select Top (42) D=DateAdd(DAY,-7+Row_Number() Over (Order By (Select Null)),#Date1) From master..spt_values n1 ) A
) A
) src
Pivot (max(d) for DOW in ([Sun],[Mon],[Tue],[Wed],[Thu],[Fri],[Sat]) )pvg
Where [Sun] is not null
and [Sat] is not null
)
-- Select * from [dbo].[tvf-Date-Calendar-Wide]('2020-04-01')
You first need to find the start of the week for the first day of the month, then the date for the end of the week that contains the last day of the month:
e.g.
SELECT WeekStart = DATEADD(DAY, -(DATEPART(WEEKDAY, '20200401')-1), '20200401'),
WeekEnd = DATEADD(DAY, 7-(DATEPART(WEEKDAY, '20200430')), '20200430');
Gives:
WeekStart WeekEnd
------------------------------
2020-03-29 2020-05-02
You wouldn't want to hard code the first and the last of the month, but these are fairly trivial things to get from a date:
DECLARE #Date DATE = '20200415';
SELECT MonthStart = DATEADD(MONTH, DATEDIFF(MONTH, 0, #Date), 0),
MonthEnd = EOMONTH(#Date);
Which returns
MonthStart MonthEnd
------------------------------
2020-04-01 2020-04-30
You can then just substitute this into the first query for week starts:
DECLARE #Date DATE = '20200401';
SELECT WeekStart = DATEADD(DAY, -(DATEPART(WEEKDAY, DATEADD(MONTH, DATEDIFF(MONTH, 0, #Date), 0))-1), DATEADD(MONTH, DATEDIFF(MONTH, 0, #Date), 0)),
WeekEnd = DATEADD(DAY, 7-(DATEPART(WEEKDAY, EOMONTH(#Date))), EOMONTH(#Date));
Which gives the same output as the first query with hard coded dates. This is very clunky though, so I would separate this out into a further step:
DECLARE #Date DATE = '20200401';
-- SET DATE TO THE FIRST OF THE MONTH IN CASE IT IS NOT ALREADY
SET #Date = DATEADD(MONTH, DATEDIFF(MONTH, 0, #Date), 0);
SELECT WeekStart = DATEADD(DAY, -(DATEPART(WEEKDAY, #Date)-1), #Date),
Weekend = DATEADD(DAY, 7-(DATEPART(WEEKDAY, EOMONTH(#Date))), EOMONTH(#Date));
Again, this gives the same output (2020-03-29 & 2020-05-02).
The next step is to fill in all the dates between that are weekdays. If you have a calendar table this is very simple
DECLARE #Date DATE = '20200415';
-- SET DATE TO THE FIRST OF THE MONTH IN CASE IT IS NOT ALREADY
SET #Date = DATEADD(MONTH, DATEDIFF(MONTH, 0, #Date), 0);
DECLARE #Start DATE = DATEADD(DAY, -(DATEPART(WEEKDAY, #Date)-1), #Date),
#End DATE = DATEADD(DAY, 7-(DATEPART(WEEKDAY, EOMONTH(#Date))), EOMONTH(#Date));
SELECT [Date], DayName = DATENAME(WEEKDAY, [Date])
FROM Calendar
WHERE Date >= #Start
AND Date <= #End
AND IsWeekday = 1
ORDER BY [Date];
If you don't have a calendar table, then I suggest you create one, but if you can't create one you can still generate this on the fly, by generating a set series numbers and adding these numbers to your start date:
DECLARE #Date DATE = '20200415';
-- SET DATE TO THE FIRST OF THE MONTH IN CASE IT IS NOT ALREADY
SET #Date = DATEADD(MONTH, DATEDIFF(MONTH, 0, #Date), 0);
DECLARE #Start DATE = DATEADD(DAY, -(DATEPART(WEEKDAY, #Date)-1), #Date),
#End DATE = DATEADD(DAY, 7-(DATEPART(WEEKDAY, EOMONTH(#Date))), EOMONTH(#Date));
-- GET NUMBERS FROM 0 - 50
WITH Dates (Date) AS
( SELECT TOP (DATEDIFF(DAY, #Start, #End))
DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY n1.n) - 1, #Start)
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n1 (n)
CROSS JOIN (VALUES (1),(1),(1),(1),(1)) n2 (n)
)
SELECT [Date], DayName = DATENAME(WEEKDAY, [Date])
FROM Dates
WHERE ((DATEPART(WEEKDAY, [Date]) + ##DATEFIRST) % 7) NOT IN (0, 1);
Just generate all possible dates -- up to 6 days before the month begins. Take the valid weekdays after the first Monday:
with dates as (
select dateadd(day, -6, convert(date, '2020-04-01')) as dte
union all
select dateadd(day, 1, dte)
from dates
where dte < '2020-04-30'
)
select dte
from (select d.*,
min(case when datename(weekday, dte) = 'Monday' then dte end) over () as first_monday
from dates d
) d
where datename(weekday, dte) not in ('Saturday', 'Sunday') and
dte >= first_monday;
declare #dateVal datetime = GETDATE(); --assign your date here
declare #monthFirstDate datetime = cast(YEAR(#dateVal) as varchar(4)) + '-' + DATENAME(mm, #dateVal) + '-' + cast(01 as varchar(2))
declare #monthLastDate datetime = DAteADD(day, -1, DATEADD(month, 1, cast(YEAR(#dateVal) as varchar(4)) + '-' + DATENAME(mm, #dateVal) + '-' + cast(01 as varchar(2))))
declare #startDate datetime = DATEADD(DAY, 2 - DATEPART(WEEKDAY, #monthFirstDate), CAST(#monthFirstDate AS DATE))
declare #enddate datetime = DATEADD(DAY, 6 - DATEPART(WEEKDAY, #monthLastDate), CAST(#monthLastDate AS DATE))
Select #startDate StartDate, #enddate EndDate
****Result**
--------------------------------------------------------------
StartDate | EndDate
-----------------------------|--------------------------------
2020-03-02 00:00:00.000 | 2020-04-03 00:00:00.000
-----------------------------|---------------------------------**

Forward dependencies #startdate #enddate=#startdate + 1 Week

Im looking for a way if the user selects
#startdate = '12-01-2015'
then
#enddate = #startdate + 1 Week
this is for my parameter, but please be aware my report is a matrix and uses each week for a sample of data so the parameter needs to automatically select
its enddate from all startdates selected
i.e
startdates in ( '12-01-2015', '19-01-2015')
then endate in ( '18-01-2015', '26-01-2015')
this is what im using for my startdate parameter
select distinct
CAST(startdate AS DATE) as Startdate
FROM [Calendar]
where DatePart(YEAR,startdate) >= 2014
order by 1 asc
Use DATEADD
#startdate = '12-01-2015'
then
#enddate = DATEADD(wk, 1, #startdate)
or if startdate variable has DATE/DATETIME type
#startdate = '12-01-2015'
then
#enddate = #startdate + 7; -- Implicit cast add 7 days
EDIT:
select distinct
CAST(startdate AS DATE) as Startdate,
DATEADD(wk, 1, CAST(startdate AS DATE)) as EndDate
FROM [Calendar]
where DatePart(YEAR,startdate) >= 2014
order by 1 asc

Accumulating data from previous year

I'm having trouble with finding a solution for below SQL-query. My first query should and does the trick of accumulating amount of last year's data.
CASE
WHEN rehuv.VER_DATUM >= dateadd(year, -1, dateadd(month, -datepart(month, #STARTDATE) + 1, #STARTDATE))
AND rehuv.VER_DATUM <= DATEADD(year, -1, #ENDDATE) THEN rehuv.BELOPP ELSE 0
END AS PREVIOUS_YEAR_ACC
But when I try to accumulate data within a specific time interval (I want the same time interval as #startdate -> #enddate but one year backwards). Sql-query:
CASE
WHEN rehuv.VER_DATUM >= DATEADD(year, -1, #STARTDATE)
AND rehuv.VER_DATUM <= DATEADD(year, -1, #ENDDATE) THEN rehuv.BELOPP ELSE 0
END AS PREVIOUS_YEAR_MONTH
Thank you in advance!
Best regards,
Simon
If you want the monthly sum of last year, you can try this:
DECLARE #STARTDATE DATETIME;
DECLARE #ENDDATE DATETIME;
SET #STARTDATE='2015-1-1';
SET #ENDDATE='2015-5-1';
DECLARE #LAST_YEAR DATE;
SET #LAST_YEAR = DATEADD(YEAR, -1, DATEADD(DAY,
DATEPART(DAYOFYEAR, GETDATE())*-1+1, CONVERT(DATE, GETDATE())));
SELECT DATEADD(MONTH, DATEPART(MONTH, VER_DATUM), #LAST_YEAR) MONTH, SUM(CASE
WHEN REHUV.VER_DATUM >= DATEADD(YEAR, -1, #STARTDATE)
AND REHUV.VER_DATUM <= DATEADD(YEAR, -1, #ENDDATE)
THEN REHUV.BELOPP ELSE 0
END ) AS PREVIOUS_YEAR_MONTH
FROM REHUV
GROUP BY DATEPART(MONTH, VER_DATUM)

query for first and last day of month

If I have the name of the month, how can I have the first and last day of that month in SQL?
I have this query to returns the month names:
DECLARE #StartDate DATETIME,
#EndDate DATETIME;
SELECT #StartDate = '20110501'
,#EndDate = '20110801';
SELECT DATENAME(MONTH, DATEADD(MONTH, x.number, #StartDate)) AS MonthName
FROM master.dbo.spt_values x
WHERE x.type = 'P'
AND x.number <= DATEDIFF(MONTH, #StartDate, #EndDate)
Result:
Now, how can i get the first and last day of that months? changing the query.
If you want a more general and simple solution:
DECLARE #startdate AS DATETIME = GETDATE()
SELECT
DATEADD(MONTH, DATEDIFF(MONTH, 0, #startdate) , 0) as startMonth,
DATEADD(SECOND, -1, DATEADD(MONTH, 1, DATEADD(MONTH, DATEDIFF(MONTH, 0, #startdate) , 0) ) ) as endMonth
Gives the result:
startMonth endMonth
----------------------- -----------------------
2013-06-01 00:00:00.000 2013-06-30 23:59:59.000
Try this :-
DECLARE #StartDate DATETIME,
#EndDate DATETIME;
SELECT #StartDate = '20110501'
,#EndDate = '20110801';
SELECT DATENAME(MONTH, DATEADD(MONTH, x.number, #StartDate)) AS MonthName,
CONVERT(VARCHAR(25),
DATEADD(dd,-(DAY(DATEADD(MONTH, x.number, #StartDate))-1),DATEADD(MONTH, x.number, #StartDate)),101) as FirstDay,
CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(DATEADD(mm,1,DATEADD(MONTH, x.number, #StartDate)))),DATEADD(mm,1,DATEADD(MONTH, x.number, #StartDate))),101) as LastDay
FROM master..spt_values x
WHERE x.type = 'P'
AND x.number <= DATEDIFF(MONTH, #StartDate, #EndDate)
Result :-
MonthName FirstDay LastDay
May 05/01/2011 05/31/2011
June 06/01/2011 06/30/2011
July 07/01/2011 07/31/2011
August 08/01/2011 08/31/2011
Result obtained taking the help from this query
This code gives you the first date of Current Month:
DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)
If you know the Month's number then:
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) + #MonthNumber, 0)
Same goes for Month's end date :
SELECT DATEADD(MILLISECOND, -1,
DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - ##MonthNumber+ 1, 0))

Returns date range by quarter?

I am looking for a query that can returns a series of date range that is one quarter long.
For example, if the input is 2/1/2013 and 3/31/2014, then the output would look like:
start end
2/1/2013 4/30/2013
5/1/2013 7/31/2013
8/1/2013 10/31/2013
11/1/2013 1/31/2014
2/1/2014 3/31/2014
Notice that the last quarter is only 2 months long. Thanks for help in advance.
Just want to add that this is what I did after I did a bit of googling. I was thinking of some more efficient way but I think this is sufficient for my purpose. The first part is to populate a date table, the second part to calculate the quarter range.
DECLARE #StartDate SMALLDATETIME
DECLARE #EndDate SMALLDATETIME
SET #StartDate = '1/1/2011'
SET #EndDate = '12/31/2011'
-- creates a date table, not needed if there is one already
DECLARE #date TABLE ( [date] SMALLDATETIME )
DECLARE #offset INT
SET #offset = 0
WHILE ( #offset < DATEDIFF(dd, #StartDate, DATEADD(dd, 1, #EndDate)) )
BEGIN
INSERT INTO #date ( [date] )
VALUES ( DATEADD(dd, #offset, #StartDate) )
SELECT #offset = #offset + 1
END ;
WITH dateCTE
AS ( SELECT ROW_NUMBER() OVER ( ORDER BY [date] ASC ) AS qID ,
[date] AS qStart ,
CASE WHEN DATEADD(dd, -1, DATEADD(q, 1, [date])) > #EndDate
THEN #EndDate
ELSE DATEADD(dd, -1, DATEADD(q, 1, [date]))
END AS qEnd
FROM #date
WHERE [date] = #StartDate
OR ( DATEDIFF(mm, #StartDate, [date]) % 3 = 0
AND DATEPART(dd, [date]) = DATEPART(dd,
#StartDate)
)
)