Returns date range by quarter? - sql

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)
)
)

Related

Is there an efficient way to break a date range into hours per day?

In SQL Server I am attempting to break a date range into hours per day and have the following bit of code which is OK for a short time frame, but rather inefficient for longer periods of time. Could anyone suggest a more efficient approach?
DECLARE #StartDate datetime = '2015-01-27 07:32:35.000',
#EndDate datetime = '2015-04-29 14:39:35.000',
#TempDate datetime = '';
SET #TempDate = #StartDate;
DECLARE #dateTimeTable TABLE (dt datetime, minCol INT);
WHILE #TempDate < #EndDate
BEGIN
INSERT INTO #dateTimeTable VALUES (CONVERT(date,#TempDate), 1)
SET #TempDate = DATEADD(minute,1,#TempDate)
END
Select dt,
FORMAT(SUM(minCol) / 60.0,'F') as Hours
from #dateTimeTable
GROUP BY dt
Thanks,
Carl
The best way would be to use recursive cte :
DECLARE #StartDate datetime = '2015-01-27 07:32:35.000',
#EndDate datetime = '2015-04-29 14:39:35.000';
WITH cte AS (
SELECT CAST(#StartDate AS DATE) startdate,DATEDIFF(minute, #StartDate, DATEADD(DAY, 1, CAST(#StartDate AS DATE) ) ) / 60.0 hours
UNION ALL
SELECT DATEADD(DAY,1, startdate), DATEDIFF(minute, DATEADD(DAY,1, startdate), CASE WHEN DATEADD(DAY,2, startdate) > #EndDate
THEN #enddate ELSE DATEADD(DAY,2, startdate) END) / 60.0
FROM cte
WHERE startdate <> CAST(#EndDate AS DATE)
)
SELECT * FROM cte
db<>fiddle here

How to find week Number based on current date and what are the dates in that week

I need to find out Week Number based on current date and what are the dates in that week.
Let say example, Current date is 27-Dec-2020, then I need to find out week no i.e. 53 and what are dates i.e. 28-Dec-2020, 29-Dec-2020....03-Jan-2021.
My expected output columns would be:
WeekNo Date Day
declare #date date = '30-Dec-2020'; --'20210101'
select #date as _date,
datepart(iso_week, #date) as isoweek,
--iso week starts on previous monday. weekday of monday is always 2 when accounting for ##datefirst
dateadd(day, -(7-2+datepart(weekday, dateadd(day, ##datefirst, #date)))%7, #date) as isoweekstartdate,
--isoweekenddate(inclusive) = add 6 days to isoweekstartdate
dateadd(day, 6, dateadd(day, -(7-2+datepart(weekday, dateadd(day, ##datefirst, #date)))%7, #date)) as isoweekenddate;
select
datepart(iso_week, #date) as isoweek,
dateadd(day, n.num, dateadd(day, -(7-2+datepart(weekday, dateadd(day, ##datefirst, #date)))%7, #date)) as isoweekdate
from
(
values (0),(1),(2),(3),(4),(5),(6)
) as n(num);
Using a simple Loop
DECLARE #date DATE, #WeekNo TINYINT, #Start TINYINT,#End TINYINT, #StartDate DATE,#EndDate DATE, #Cdate DATE
SET #date='27-DEC-2020'
SET #StartDate = DATEADD(DAY,-7,#date)
set #EndDate = DATEADD(DAY,7,#date)
SET #WeekNo = DATEPART(WEEK,#date)
SET #Start = 1
SET #End = DATEDIFF(DAY,#StartDate,#EndDate)
DECLARE #dates TABLE(WeekNo TINYINT, [Date] DATE, [Day] VARCHAR(20))
WHILE(#Start <=#End)
BEGIN
SET #Cdate = DATEADD(DAY,#Start,#StartDate)
IF DATEPART(WEEK,#Cdate)=#WeekNo
INSERT #dates VALUES(#WeekNo, #Cdate, DATENAME(WEEKDAY , #Cdate))
SET #Start = #Start + 1
END
SELECT * FROM #dates

Find the Same day of Previous Year Given by Current Year Date in SQL Server

I am working with SQL Server, The scenario is to find out the Same Day's Date of Previous Year as of Today's Day.
Suppose 2014-03-06 is Today Date and Day is Thursday I want to Find the Same day in Previous lies in the same week .which is 2013-03-07
can any body help?
HERE is what i Have Written:
DECLARE #DateFrom AS DATETIME
DECLARE #DateTo AS DATETIME
SET #DateFrom = '2014-01-01'
SET #DateTo = '2014-02-10'
DECLARE #Count AS INT
SET #Count = DATEDIFF(DAY, #DateFrom, #DateTo)
CREATE TABLE #current_year /*This Year*/
(
[Date] DATETIME ,
WeekNum INT,
[Day] VARCHAR(20),
Data INT
)
CREATE TABLE #last_year /*This Year -1*/
(
[Date] DATETIME ,
WeekNum INT,
[Day] VARCHAR(20),
Data INT
)
WHILE ( #Count > 0 )
BEGIN
INSERT INTO #current_year
VALUES ( CONVERT(VARCHAR(10), #DateFrom, 101),
DATEPART(week,#DateFrom),
DATENAME(weekday, #DateFrom),#Count)
INSERT INTO #last_year
VALUES ( CONVERT(VARCHAR(10), DATEADD(YEAR, -1, #DateFrom), 101),
DATEPART(week,DATEADD(YEAR,1,#DateFrom)),
DATENAME(weekday, DATEADD(YEAR, -1, #DateFrom)),#Count)
SET #DateFrom = DATEADD(day, 1, #DateFrom)
SET #Count = #Count - 1
END
SELECT * from #current_year
SELECT * from #last_year
SELECT CONVERT(varchar(10),#current_year.[Date],111) AS CYDate,
--ISNULL(CONVERT(varchar(10),#last_year.[Date],111) ,/*CONVERT(varchar(10),DateAdd(dd, 1, DATEADD(yy, -1, #current_year.Date)),111)*/) AS LYDate
--CONVERT(varchar(10),#last_year.[Date],111) AS LYDate
Coalesce(CONVERT(varchar(10),#last_year.[Date],111) ,DateAdd(dd, 1, DATEADD(yy, -1, #current_year.Date))) AS LYDate,
#current_year.Data AS CD,
#last_year.Data AS LD
FROM #current_year
--LEFT JOIN #last_year ON #last_year.WeekNum = #current_year.WeekNum
-- AND #last_year.[Day] = #current_year.[Day]
Left JOIN #last_year ON #last_year.WeekNum = DatePart(wk, GETDATE())
DROP TABLE #current_year
DROP TABLE #last_year
Here is the Output:
Here is the output after adding your solution, now in left join it excludes (NULL) data of previous year
Basically you need to find difference in days between same dates in this and previous years, then understand "day difference" by mod 7, and sum it with date in previous year:
DECLARE #now DATETIME
SET #now = '2014-03-06'
SELECT CAST (DATEADD(YEAR, -1, #now) + (CAST (#now as INT) - CAST (DATEADD(YEAR, -1, #now) AS INT)) % 7 AS DATE)
Returns
2013-03-07
Try
DECLARE #now Date
SET #now = '2014-06-03'
SELECT DATEADD(week, -52, #now)
SELECT DateName(dw, DATEADD(yy, -1, GETDATE()))
gives Wednesday
SELECT DateName(dw, DateAdd(dd, 1, DATEADD(yy, -1, GETDATE())))
gives Thursday
edit:
SELECT DateAdd(dd, 1, DATEADD(yy, -1, GETDATE()))
gives '2013-03-07 17:30:16.590'
you need to cast the date as per you requirement..
update:
change this part with,
Left JOIN #last_year ON #last_year.WeekNum = DatePart(wk, GETDATE())
in your case:
Left JOIN #last_year ON DatePart(wk, #last_year.[Date]) = DatePart(wk, #current_year.[Date])
update 2:
Left JOIN #last_year ON (MONTH(#last_year.[Date])=MONTH(#current_year.[Date]) and Day(#last_year.[Date])=Day(#current_year.[Date]))
Output:
or
output:
Left JOIN #last_year ON (#last_year.WeekNum = #current_year.WeekNum and #last_year.[Day] = #current_year.[Day])
choose which ever is your required output.
DECLARE #Date DATE = '2014-03-06'
SELECT DATEADD(WEEK,-52,#Date)
Retrun value :
2013-03-07

Number of Weeks between Parameter Dates - Group by Week - SQL

I have 2 parameter in my stored procedure #FromDate and #ToDate, I want to work out the number of weeks between #FromDate and #ToDate, #FromDate is always a Monday while #ToDate is always a Sunday but the dates could cover weeks, months etc.
Currently I have DATEDIFF(WW, #FromDate, #ToDate)
Which returns say 4 but I would like it to return 1,2,3 and 4 in 4 rows rather than just 1 row.
What is the best way to achieve this?
If I've got it right:
With T( Number ) as
(
Select 0 as Number
union all
Select Number + 1
from T
where Number < DATEDIFF(WW, #FromDate, #ToDate)
)
SELECT NUMBER FROM T WHERE Number>0;
SQLFiddle demo
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = GETUTCDATE() - 90
SET #EndDate = GETUTCDATE()
DECLARE #WeekNumber INT
SET #WeekNumber = DATEDIFF(WW, #StartDate, #EndDate) ;
WITH CTE_WeekNumber ( WeekNumber )
AS ( SELECT 1
WHERE #WeekNumber >= 1
UNION ALL
SELECT WeekNumber + 1
FROM CTE_WeekNumber
WHERE #WeekNumber >= WeekNumber + 1
)
SELECT *
FROM CTE_WeekNumber

How can I rewrite this as a select statement using group by instead of using a loop

I am revisiting some old code I wrote for a report when I was still very new to SQL (MSSQL). It does what it is supposed to but its not the prettiest or most efficient.
The dummy code below mimics what I currently have in place. Here I am trying to get counts for the number of contracts that are open over the last 5 weeks. For this example a contract is considered open if the start date of the contract happens before of during the given week and the end date happens during or after the given week.
dbo.GetWeekStart(#Date DATETIME, #NumOfWeeks INT, #FirstDayOfWeek CHAR(3)) is a function that will return the first day of each week based on the date provided for a specified number of weeks. ie SELECT * FROM dbo.GetWeekStart('20120719', -2, 'MON') will return the 2 mondays prior to July 19, 2012.
How can I simplify this? I think there is someone to do this without a loop but I have not been able to figure it out.
DECLARE #RunDate DATETIME,
#Index INT,
#RowCount INT,
#WeekStart DATETIME,
#WeekEnd DATETIME
DECLARE #Weeks TABLE
(
WeekNum INT IDENTITY(0,1),
WeekStart DATETIME,
WeekEnd DATETIME
)
DECLARE #Output TABLE
(
WeekStart DATETIME,
OpenContractCount INT
)
SET #RunDate = GETDATE()
INSERT INTO #Weeks (WeekStart, WeekEnd)
SELECT WeekStart,
DATEADD(ss,-1,DATEADD(ww,1,WeekStart))
FROM dbo.[GetWeekStart](#RunDate, -5, 'MON')
SET #RowCount = (SELECT COUNT(*) FROM #Weeks)
SET #Index = 0
WHILE #Index < #RowCount
BEGIN
SET #WeekStart = (SELECT WeekStart FROM #Weeks WHERE WeekNum = #Idx)
SET #WeekEnd = (SELECT WeekEnd FROM #Weeks WHERE WeekNum = #Idx)
INSERT INTO #Output (WeekStart, OpenContractCount)
SELECT #WeekStart,
COUNT(*)
FROM Contracts c
WHERE c.StartDate <= #WeekEnd
AND ISNULL(c.EndDate, GETDATE()) >= #WeekStart
SET #Index = #Index + 1
END
SELECT * FROM #Output
I see no reason why this wouldn't work:
DECLARE #RunDate DATETIME = GETDATE()
SELECT WeekStart, COUNT(*)
FROM Contracts c
INNER JOIN dbo.[GetWeekStart](#RunDate, -5, 'MON')
ON c.StartDate < DATEADD(WEEK, 1, WeekStart)
AND (c.EndDate IS NULL OR c.EndDate >= #WeekStart)
GROUP BY WeekStart
I am not sure how you are generating your dates within your function, just in case you are using a loop/recursive CTE I'll include a query that doesn't use loops/cursors etc.
DECLARE #RunDate DATETIME = GETDATE()
-- SET DATEFIRST AS 1 TO ENSURE MONDAY IS THE FIRST DAY OF THE WEEK
-- CHANGE THIS TO SIMULATE CHANGING YOUR WEEKDAY INPUT TO db
SET DATEFIRST 1
-- SET RUN DATE TO BE THE START OF THE WEEK
SET #RunDate = CAST(DATEADD(DAY, 1 - DATEPART(WEEKDAY, #RunDate), #RunDate) AS DATE)
;WITH Weeks AS
( SELECT TOP 5 -- CHANGE THIS TO CHANGE THE WEEKS TO RUN
DATEADD(WEEK, 1 - ROW_NUMBER() OVER(ORDER BY Object_ID), #RunDate) [WeekStart]
FROM sys.All_Objects
)
SELECT WeekStart, COUNT(*)
FROM Contracts c
INNER JOIN Weeks
ON c.StartDate < DATEADD(WEEK, 1, WeekStart)
AND (c.EndDate IS NULL OR c.EndDate >= #WeekStart)
GROUP BY WeekStart
Did this quick but it should work
/*CTE generates Start & End Dates for 5 weeks
Start Date = Sunday of week # midnight
End Date = Sunday of next week # midnight
*/
WITH weeks
AS ( SELECT DATEADD(ww, -4,
CAST(FLOOR(CAST(GETDATE() - ( DATEPART(dw,
GETDATE()) - 1 ) AS FLOAT)) AS DATETIME)) AS StartDate
UNION ALL
SELECT DATEADD(wk, 1, StartDate)
FROM weeks
WHERE DATEADD(wk, 1, StartDate) <= GETDATE()
)
SELECT w.StartDate ,
COUNT(*) AS OpenContracts
FROM dbo.Contracts c
LEFT JOIN weeks w ON c.StartDate < DATEADD(d, 7, w.StartDate)
AND ISNULL(c.EndDate, GETDATE()) >= w.StartDate
GROUP BY w.StartDate