Find numbers of weeks in date range(from date-To date) in SQL SERVER - sql

I have two dates FromDate and ToDate. Start is always Monday and ToDate is always Friday.
I want to find number of weeks separately not just count.
I want to show week1, week2, week3, etc report in given date range. (ie Weekly product counts).
I have tried this :
SELECT * FROM WeekDate
WHERE DATEDIFF(Week,GETDATE(),'2013-09-20')=0
It gives all values not just for week 0.
Can any one help me about this?
Table structure
CREATE TABLE [dbo].[WeekDate]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[Counter] [int] NULL,
[CDate] [datetime] NOT NULL
) ON [PRIMARY]
and data is
1 1 2013-09-17 00:00:00.000
3 2 2013-09-18 00:00:00.000
4 6 2013-09-19 00:00:00.000
5 2 2013-09-23 00:00:00.000
6 3 2013-09-24 00:00:00.000
7 4 2013-10-01 00:00:00.000

Hard to say without example of output, but could be something like this:
select
sum(Counter),
datediff(day, #FromDate, CDate) / 7 as ddiff
from WeekDate
-- if you need to filter out rows
where CDate >= #FromDate and CDate <= #ToDate
group by datediff(day, #FromDate, CDate) / 7
sql fiddle demo

I think you want sum of product by weekly and if so then try this as you said i have taken start date as Monday and end date as Friday
declare #StartDate datetime,
#EndDate datetime;
select #StartDate = '2013-09-16',
#EndDate = '2013-10-04';
;with cte as
(
select #StartDate StartDate,
DATEADD(wk, DATEDIFF(wk, 0, #StartDate), 6) EndDate
union all
select dateadd(ww, 1, StartDate),
dateadd(ww, 1, EndDate)
from cte
where dateadd(ww, 1, StartDate)<= #EndDate
)
select (Select COUNT(Cdate) from WeekDate where Cdate between StartDate and EndDate) count1 , StartDate , EndDate
from cte

Related

How do I calculate amount of time in each week between two dates - SQL Server

[EDITED TO SIMPLIFY]
I have 500+ records. All of which have a reference number, a start date, an end date and a total machining time.
Ref StartDate EndDate MachineTimeHours
123 24/01/2020 30/01/2020 28
321 25/02/2020 27/02/2020 18
Starting at the start date, I need to calculate how many machining hours fall into 1 week and how many fall into the next.
Our working days are Monday to Thursday 8 Hours & Friday 4 Hours.
Ref 321 has a start of 25/2 which is a Tuesday and a finish date of 27/2 which is a Thursday in the same week. This will calculate as all 18 hours being in the same week.
Ref 123 has a start of 24/01. This is a Friday in Week 4 of 2020.
Based on my rules, that would be 4 hours in week 4 and 24 Hours in week 5.
I have a table called 'DatesList' which has all days on it (as well as week number and working hours).
I need my table to list each record for each week and I'll do the grouping on a separate report.
In effect I'd like
Ref StartDate EndDate MachineTimeHours Week Hours
123 24/01/2020 30/01/2020 28 4 4
123 24/01/2020 30/01/2020 28 5 24
321 25/02/2020 27/02/2020 18 9 18
You can start with creating some reference tables.
For the example those are just temporary tables.
Reference data:
--
-- Reference tables
--
CREATE TABLE #ref_calendar
(
CalDate DATE PRIMARY KEY,
DayOfWeek SMALLINT NOT NULL,
WeekNr SMALLINT NOT NULL,
IsHoliday BIT NOT NULL DEFAULT 0
);
DECLARE #year int = 2020;
SET DATEFIRST 1; -- 1: monday start
;WITH RCTE_DATES AS
(
SELECT
DATEFROMPARTS(#year, 1, 1) AS caldate
UNION ALL
SELECT dateadd(day, 1, caldate)
FROM RCTE_DATES
WHERE caldate <= DATEFROMPARTS(#year, 12, 31)
)
INSERT INTO #ref_calendar (CalDate, DayOfWeek, WeekNr)
SELECT
caldate,
DATEPART(weekday, caldate) AS DayOfWeek,
DATEPART(week, caldate) AS WeekNr
FROM rcte_dates c
WHERE NOT EXISTS
(
SELECT 1
FROM #ref_calendar ref
WHERE ref.CalDate = c.caldate
)
OPTION (MAXRECURSION 366);
CREATE TABLE #ref_workhours
(
Id INT IDENTITY(1,1) PRIMARY KEY,
DayOfWeek SMALLINT NOT NULL,
WorkHours DECIMAL(4,2) NOT NULL,
ActiveFrom DATE NOT NULL DEFAULT GetDate(),
ActiveTill DATE
);
INSERT INTO #ref_workhours
(DayOfWeek, WorkHours) VALUES
(1, 8.0), (2, 8.0), (3, 8.0), (4, 8.0), (5, 4.0),
(6, 0), (7, 0);
Some sample data:
--
-- Sample data
--
CREATE TABLE YourDateRangeTable
(
Id INT IDENTITY(1,1) PRIMARY KEY,
JobNumber INT NOT NULL,
PartNumber VARCHAR(8) NOT NULL,
Machine CHAR(3) NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL
);
INSERT INTO YourDateRangeTable
(JobNumber, PartNumber, Machine, StartDate, EndDate) values
(12345, 'XYZ321', 'DL8', '2020-01-24', '2020-01-30');
Then you can run a query that uses the reference tables.
SELECT JobNumber, PartNumber, Machine
, YEAR(cal.CalDate) AS [Year]
, cal.WeekNr AS [Week]
, SUM(w.WorkHours) AS [Hours]
FROM YourDateRangeTable t
JOIN #ref_calendar cal
ON cal.CalDate >= t.StartDate
AND cal.CalDate < t.EndDate
JOIN #ref_workhours w
ON w.DayOfWeek = cal.DayOfWeek
GROUP BY JobNumber, PartNumber, Machine
, YEAR(cal.CalDate), cal.WeekNr;
Returns:
JobNumber PartNumber Machine Year Week Hours
12345 XYZ321 DL8 2020 4 4.00
12345 XYZ321 DL8 2020 5 24.00
A test on db<>fiddle here
You can get all the detes of two given date and also weeknumber and a case statement for the working hour. Based on the result from the inner query write an outer query which will give the sum of total working hour.
Here is the given query.
DECLARE #MinDate DATE = '20200124',
#MaxDate DATE = '20200130'
--Fri Week 4 = 4 hours
--Mon Week 5 = 8 hours
--Tue Week 5 = 8 hours
--Wed Week 5 = 8 hours
Select WeekNo, SUM(WorkingHour) as TotalWorkingHour from(
Select [DATE], DATEPART(WEEK, [DATE]) - DATEPART(WEEK, DATEADD(MM, DATEDIFF(MM,0,[DATE]), 0))+ 1 as WeekNo,
DATENAME(weekday, [DATE]) as WeekDay, Case DATENAME(weekday, [DATE])
when 'Friday' then 4
when 'Monday' then 8
when 'Tuesday' then 8
when 'Wednesday' then 8
else 0
end as WorkingHour from(
SELECT TOP (DATEDIFF(DAY, #MinDate, #MaxDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #MinDate)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
)a
)b group by WeekNo
It will give the result as below:
WeekNo TotalWorkingHour
------------------------
4 4
5 24
You can find the demo Here.

How to get the week number, start date and end date of Compelete year in SQL Server?

How to get the complete week number, startdate and enddate of complete year.
Note
Week starts from Sunday to Saturday
at the same time week no 1 (ex: 01-01-2020 to 04-01-2020) and last week should be (ex:27-12-2020 to 31-12-2020)
Expecting result
WeekNo WeekStartDate WeekEndDate
===================================
1 2019-01-01 2019-01-05
2 2019-01-06 2019-01-12
3 2019-01-13 2019-01-19
4 2019-01-20 2019-01-26
5 2019-01-27 2019-02-02
6 2019-02-03 2019-02-09
7 2019-02-10 2019-02-16
8 2019-02-17 2019-02-23
9 2019-02-24 2019-03-02
...
...upto end of the year
Actually I tried this one also rextester
If you are interested in 2019 only, then the following code will produce exactly what you are looking for:
DECLARE #weekNum INT = 1;
WITH Weeks AS (
SELECT #weekNum AS WeekNo
UNION ALL
SELECT WeekNo + 1 FROM Weeks WHERE WeekNo + 1 <= 53
)
SELECT WeekNo,
CASE
WHEN WeekStartDate < '2019-01-01' THEN CONVERT(DATE, '2019-01-01')
ELSE CONVERT(DATE, WeekStartDate)
END AS WeekStartDate,
CASE
WHEN WeekEndDate > '2019-12-31' THEN CONVERT(DATE, '2019-12-31')
ELSE CONVERT(DATE, WeekEndDate)
END AS WeekEndDate
FROM (
SELECT WeekNo,
DATEADD(WEEK, WeekNo - 1, '2018-12-30') AS WeekStartDate,
DATEADD(WEEK, WeekNo - 1, '2019-01-05') AS WeekEndDate
FROM Weeks
) a
OUTPUT:
WeekNo WeekStartDate WeekEndDate
1 2019-01-01 2019-01-05
2 2019-01-06 2019-01-12
3 2019-01-13 2019-01-19
4 2019-01-20 2019-01-26
5 2019-01-27 2019-02-02
6 2019-02-03 2019-02-09
7 2019-02-10 2019-02-16
8 2019-02-17 2019-02-23
...
51 2019-12-15 2019-12-21
52 2019-12-22 2019-12-28
53 2019-12-29 2019-12-31
Edit following OP comment about variable start and end dates
Following OP's comment about varying start and end dates, I've revisited the code and made it such that is can work between any two dates:
DECLARE #startDate DATE = CONVERT(DATE, '2019-01-01');
DECLARE #endDate DATE = CONVERT(DATE, '2019-12-31');
DECLARE #weekNum INT = 1;
WITH Weeks AS (
SELECT #weekNum AS WeekNo
UNION ALL
SELECT WeekNo + 1 FROM Weeks WHERE WeekNo + 1 <= DATEDIFF(WEEK, #StartDate, #EndDate) + 1
)
SELECT WeekNo,
CASE
WHEN WeekStartDate < #startDate THEN #startDate
ELSE CONVERT(DATE, WeekStartDate)
END AS WeekStartDate,
CASE
WHEN WeekEndDate > #endDate THEN #endDate
ELSE CONVERT(DATE, WeekEndDate)
END AS WeekEndDate
FROM (
SELECT WeekNo,
DATEADD(WEEK, WeekNo - 1, OffsetStartDate) AS WeekStartDate,
DATEADD(WEEK, WeekNo - 1, OffsetEndDate) AS WeekEndDate
FROM Weeks
INNER JOIN (
SELECT CASE
WHEN DATEPART(WEEKDAY, #startDate) = 1 THEN #startDate
ELSE DATEADD(DAY, 1 - DATEPART(WEEKDAY, #startDate), #startDate)
END AS OffsetStartDate,
CASE
WHEN DATEPART(WEEKDAY, #startDate) = 1 THEN DATEADD(DAY, 6, #startDate)
ELSE DATEADD(DAY, 7 - DATEPART(WEEKDAY, #startDate), #startDate)
END AS OffsetEndDate
) a ON 1 = 1
) a
Simply modify #startDate and #endDate to reflect the desired start and end dates. The format of the string is YYYY-MM-DD.
This will output a variable number of weeks between the two dates, starting and ending on the specified date (creating partial weeks as needed). Hopefully, as per the requirement.
Slightly update on above answer of Martin. You can pass #startDate and #endDate based on your preference.
DECLARE #startDate DATETIME = '2019-01-01'
DECLARE #endDate DATETIME = '2021-01-01'
DECLARE #totalWeeks BIGINT= NULL
SELECT #totalWeeks =datediff(ww,#startdate,#enddate)
DECLARE #weekNum INT = 1;
WITH Weeks AS (
SELECT #weekNum AS WeekNo
UNION ALL
SELECT WeekNo + 1 FROM Weeks WHERE WeekNo + 1 <= #totalWeeks
)
SELECT WeekNo,
CASE
WHEN WeekStartDate < #startDate THEN CONVERT(DATE, '2019-01-01')
ELSE CONVERT(DATE, WeekStartDate)
END AS WeekStartDate,
CASE
WHEN WeekEndDate > #endDate THEN CONVERT(DATE, '2019-12-31')
ELSE CONVERT(DATE, WeekEndDate)
END AS WeekEndDate
FROM (
SELECT WeekNo,
DATEADD(WEEK, WeekNo - 1, #startDate) AS WeekStartDate,
DATEADD(WEEK, WeekNo - 1, #endDate) AS WeekEndDate
FROM Weeks
) a
OPTION (MAXRECURSION 1000);

Get average days open for issues per day (SQL)

I'm trying to create a chart in PowerBI that shows the average days open of issues in a database at each given time between two dates.
CREATE TABLE Issues
(IssueID int,IssueName varchar(10),created datetime, closed datetime);
INSERT INTO Issues
VALUES
(1,'a','2012-01-01 00:00:00', '2012-05-01 00:00:00'),
(2,'b','2012-03-01 00:00:00', '2012-06-01 00:00:00');
My first query shows all data in the database:
SELECT IssueID,
DATEDIFF(DAY,created,ISNULL(closed,GETDATE())) AS 'Days_Open'
FROM Issues
Results:
IssueID Days_Open
1 4
2 3
What I want to find is for each day, the issues open that day and average days open?
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2012-01-01'
SET #EndDate = '2012-07-01'
Ex:
Issues open each day
Date IssueID Days_Open
2012-01-01 1 0
2012-02-01 1 1
2012-03-01 1 2
2012-03-01 2 0
2012-04-01 1 3
2012-04-01 2 1
2012-05-01 1 4
2012-05-01 2 2
2012-06-01 2 3
Day 07 has no issues
Average
Date Average_Days_Open
2012-01-01 0 (1 issue just created)
2012-02-01 1 (1 issue - 1 day old)
2012-03-01 1 (2 issues - (2+0)/2 = 1)
2012-04-01 2 (2 issues - (3+1)/2 = 2)
2012-05-01 3 (2 issues - (4+2)/2 = 3)
2012-06-01 3 (1 issue - (0+3)/1 = 3)
2012-07-01 0 (Since there were no issues that day)
If i can get the data from both then I should be able to create a line chart in PowerBi similar to this:
Chart
Can someone please help out?
Based on syntax I guess it is SQL Server:
1) Query:
WITH CTE_DatesTable
AS
(
SELECT CAST('20120101' as datetime) AS [date]
UNION ALL
SELECT DATEADD(dd, 1, [date])
FROM CTE_DatesTable
WHERE DATEADD(dd, 1, [date]) <= '20120106'
)
SELECT [date] , i.IssueId, DATEDIFF(DAY,i.created,d.[date]) AS Days_Open
FROM CTE_DatesTable d
JOIN Issues i
ON d.date BETWEEN i.created AND i.closed
ORDER BY [date], IssueId
OPTION (MAXRECURSION 0);
DBFiddle Demo
2) Query (average):
WITH CTE_DatesTable
AS
(
SELECT CAST('20120101' as datetime) AS [date]
UNION ALL
SELECT DATEADD(dd, 1, [date])
FROM CTE_DatesTable
WHERE DATEADD(dd, 1, [date]) <= '20120106'
)
SELECT [date] ,AVG( DATEDIFF(DAY,i.created,d.[date]))
FROM CTE_DatesTable d
JOIN Issues i
ON d.date BETWEEN i.created AND i.closed
GROUP BY [date]
ORDER BY [date]
OPTION (MAXRECURSION 0);
DBFiddle Demo2
The whole idea is to generate calendar table.
If you need dates that are out of range you could use LEFT JOIN:
WITH CTE_DatesTable
AS
(
SELECT CAST('20120101' as datetime) AS [date]
UNION ALL
SELECT DATEADD(dd, 1, [date])
FROM CTE_DatesTable
WHERE DATEADD(dd, 1, [date]) <= '20120107'
)
SELECT [date] , COALESCE(AVG( DATEDIFF(DAY,i.created,d.[date])),0)
FROM CTE_DatesTable d
LEFT JOIN Issues i
ON d.date BETWEEN i.created AND i.closed
GROUP BY [date]
ORDER BY [date]
OPTION (MAXRECURSION 0);
DBFiddle Demo3

Generate list of dates based on the day and occurrence of the month

I want to generate based on the day of the week and number of the occurrence in the month of a date, a list of dates for each month between two dates. Assuming I have a #StartDate = 2016/04/01 and #EndDate = 2016/09/01, i check that #StartDate is on a first Friday of April, then to #EndDate will create dates for all first Friday of each month:
2016/05/06
2016/06/03
2016/07/01
2016/08/05
In case #StartDate = 2016/04/12 and #EndDate = 2016/09/01, I note that the #StartDate is the second Tuesday of April, then went to get every second Tuesday Tuesday of each month :
2016/05/10
2016/06/14
2016/07/12
2016/08/09
In case#StartDate = 2016/04/28 and #EndDate = 2016/09/01, I note that the #StartDate is on the last Thursday of the month of April:
2016/05/26
2016/06/30
2016/07/28
2016/08/25
In the last case, i need to verify the number of weeks of each month, because exists months only with 4 weeks or with 5 weeks and i want the last occurrence.
What I have done? I found a code that gives me every Monday in the third week of the month, and i adopted a little to get a #StartDate and #EndDate:
;with
filler as (select row_number() over (order by a) a from (select top 100 1 as a from syscolumns) a cross join (select top 100 1 as b from syscolumns) b),
dates as (select dateadd(month, a-1, #StartDate ) date from filler where a <= 1000 and dateadd(month, a-1, #StartDate) < #EndDate),
FirstMonday as (
select dateadd(day, case datepart(weekday,Date) /*this is the case where verify the week day*/
when 1 then 1
when 2 then 0
when 3 then 6
when 4 then 5
when 5 then 4
when 6 then 3
when 7 then 2
end, Date) as Date
,case when datepart(weekday, #StartDate) = 1 then 3 else 2 end as Weeks /*here i verify the number of weeks to sum in the next date*/
from dates
)
select dateadd(week, Weeks, Date) as ThirdMonday
from FirstMonday
So, it is:
set #NumSemana = datepart(day, datediff(day, DATEADD(mm, DATEDIFF(mm,0,#StartDate), 0), #StartDate)/7 * 7)/7 + 1;
WITH AllDays
AS ( SELECT #StartDate AS [Date], DATEPART(month, #StartDate) as validMonth
UNION ALL
SELECT DATEADD(week, 1, [Date]),
iif(DATEPART(month,DATEADD(week, 1, [Date])) < validMonth + #PeriodicityRepeat, validMonth, validMonth + #PeriodicityRepeat)
FROM AllDays
WHERE
DATEPART(month,[Date]) <= DATEPART(month,#EndDate)
and DATEPART(year,[Date]) <= DATEPART(year,#EndDate)
),
rankedDays
AS(
SELECT [Date], validMonth,
row_number() over ( partition by DATEPART( month, [Date]) order by [Date]) ascOrder,
row_number() over ( partition by DATEPART( month, [Date]) order by [Date] desc) descOrder
FROM AllDays
WHERE DATEPART(month, [Date]) = validMonth
)
select [Date]
from rankedDays
where ((ascOrder = #NumSemana and #NumSemana <=4 )
or (descOrder = 1 and #NumSemana = 5)
or [Date] = #StartDate )
and [Date] < #EndDate
OPTION (MAXRECURSION 0)
Query:
DECLARE #StartDate DATE = '2016-04-28',
#EndDate DATE = '2016-09-01'
;WITH dates AS (
SELECT DATEADD(week, -5, #StartDate) as date_
UNION ALL
SELECT DATEADD(week,1,date_)
FROM dates
WHERE DATEADD(week,1,date_) < #enddate
), final AS (
SELECT ROW_NUMBER() OVER (PARTITION BY DATEPART(year,date_), DATEPART(month,date_) ORDER BY date_ ASC) as RN,
date_
FROM dates
), weeks AS (
SELECT *
FROM (VALUES
(1,1),
(2,2),
(3,3),
(4,4),
(4,5),
(5,4),
(5,5)
) as t(w1,w2)
WHERE w1 = (SELECT RN FROM final WHERE date_ = #StartDate)
)
SELECT MAX(date_) as date_
FROM final f
INNER JOIN weeks w ON f.RN = w.w2
WHERE date_ between #StartDate and #EndDate AND date_ != #StartDate
GROUP BY DATEPART(YEAR,date_), DATEPART(MONTH,date_)
ORDER BY MAX(date_) ASC
Outputs:
For #StartDate = 2016/04/01 and #EndDate = 2016/09/01
date_
----------
2016-05-06
2016-06-03
2016-07-01
2016-08-05
(4 row(s) affected)
For #StartDate = 2016/04/12 and #EndDate = 2016/09/01
date_
----------
2016-05-10
2016-06-14
2016-07-12
2016-08-09
(4 row(s) affected)
For #StartDate = 2016/04/28 and #EndDate = 2016/09/01
date_
----------
2016-05-26
2016-06-30
2016-07-28
2016-08-25
(4 row(s) affected)

SQL - creating a list of custom dates between two dates

I am having trouble compiling a query than can do the following:
I have a table which has a startDate and endDate [tblPayments]
I have a column which stores a specific paymentDay [tblPayments]
Data
paymentID startDate endDate paymentDay
1 2016-01-01 2016-12-31 25
2 2015-06-01 2016-06-30 16
I am trying to generate a SELECT query which will split this specific table into separate lines based on the amount of months between these two dates, and set the paymentDay as the day for these queries
Example Output
paymentID expectedDate
1 2016-01-25
1 2016-02-25
1 2016-03-25
1 2016-04-25
1 2016-05-25
1 2016-06-25
1 2016-07-25
1 2016-08-25
1 2016-09-25
1 2016-10-25
1 2016-11-25
1 2016-12-25
2 2015-06-16
2 2015-07-16
2 2015-08-16
2 2015-09-16
2 2015-10-16
2 2015-11-16
2 2015-12-16
2 2016-01-16
2 2016-02-16
2 2016-03-16
2 2016-04-16
2 2016-05-16
I have found a query which will select the months between these dates but its adapting it to my table above, and multiple startDates and endDates I am struggling with
spliting the months
declare #start DATE = '2015-01-01'
declare #end DATE = '2015-12-31'
;with months (date)
AS
(
SELECT #start
UNION ALL
SELECT DATEADD(MM,1,date)
from months
where DATEADD(MM,1,date)<=#end
)
select Datename(MM,date) from months
This query is limited to just one startDate and endDate, so I haven't expanded it to change the DAY of the date.
Use a date table and a simple inner join
DECLARE #tblPayments table (paymentID int identity(1,1), startDate date, endDate date, paymentDay int)
INSERT #tblPayments VALUES
('2016-01-01', '2016-12-31', 25),
('2015-06-01', '2016-06-30', 16)
;WITH dates AS -- Build date within the range of startDate and endDate
(
SELECT MIN(startDate) AS Value, MAX(endDate) AS MaxDate FROM #tblPayments
UNION ALL
SELECT DATEADD(DAY, 1, Value), MaxDate
FROM dates WHERE DATEADD(DAY, 1, Value) <= MaxDate
)
SELECT pay.paymentID, dates.Value AS expectedDate
FROM
#tblPayments pay
INNER JOIN dates ON
dates.Value BETWEEN pay.startDate AND pay.endDate
AND DAY(dates.Value) = paymentDay
OPTION (maxrecursion 0)
I would create an in memory calendar table and then perform a simple query by joining to that:
-- Create a table with all the dates between the min and max dates in the
-- data table
DECLARE #Calendar TABLE
(
[CalendarDate] DATETIME
)
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SELECT #StartDate = MIN(startdate), #EndDate = MAX(enddate) FROM YourDataTable
WHILE #StartDate <= #EndDate
BEGIN
INSERT INTO #Calendar (CalendarDate)
SELECT #StartDate
SET #StartDate = DATEADD(dd, 1, #StartDate)
END
-- Join to return only dates between the start and end date that match the Payment Day
SELECT D.PaymentId, C.CalendarDate FROM YourDataTable D
INNER JOIN #Calendar C ON C.CalendarDate BETWEEN D.StartDate AND D.EndDate
AND DATEPART(day, C.CalendarDate) = D.PaymentDay