I'm trying to get the total days of datediff from different items. There are few conditions:
Some dates are before the StartDate '2020/04/01'
Some dates are after the EndDate '2020/04/30'
Delivery Dates could be before/after or between Start/End dates
Collection Dates could be before/after or between Start/End dates
For example, items have different Delivery and Collection dates between StartDate and EndDate or they could be the same but if the Delivery and Collection dates are not in between Startdate and EndDate so the result should be the datediff between StartDate and EndDate.
In my case the StartDate is 2020/04/01 and EndDate is 2020/04/30
Here is my query:
DECLARE #StartDate DATE
DECLARE #EndDate DATE
SET #StartDate = '2020/04/01'
SET #EndDate = '2020/04/30'
select distinct itemno,
sum ( case when deliverydate between #StartDate and #EndDate and collectiondate between #StartDate and #enddate then
(DATEDIFF(day,deliverydate,collectiondate)-(DATEDIFF(week,deliverydate,collectiondate)*2))+1-(CASE WHEN DATENAME(weekday, deliverydate) = 'Sunday' THEN 1 ELSE 0 END)
- (CASE WHEN DATENAME(weekday, collectiondate) = 'Saturday' then 1 else 0 end)
when deliverydate between #StartDate and #enddate and collectiondate >= #enddate then
(DATEDIFF(day,deliverydate,#EndDate)-(DATEDIFF(week,deliverydate,#EndDate)*2))+1-(CASE WHEN DATENAME(weekday, deliverydate) = 'Sunday' THEN 1 ELSE 0 END)
- (CASE WHEN DATENAME(weekday,#EndDate) = 'Saturday' then 1 else 0 end)
when deliverydate <= #StartDate and ci.docdate#5 >= #EndDate then
(DATEDIFF(day,#StartDate,#EndDate)-(DATEDIFF(week,#StartDate,#EndDate)*2))+1-(CASE WHEN DATENAME(weekday, #StartDate) = 'Sunday' THEN 1 ELSE 0 END)
- (CASE WHEN DATENAME(weekday, #endDate) = 'Saturday' then 1 else 0 end)
when deliverydate <= #StartDate and ci.docdate#5 between #startdate and #EndDate then
(DATEDIFF(day,#StartDate,ci.docdate#5)-(DATEDIFF(week,#StartDate,ci.docdate#5)*2))+1-(CASE WHEN DATENAME(weekday,ci.docdate#5) = 'Sunday' THEN 1 ELSE 0 END)
- (CASE WHEN DATENAME(weekday, #StartDate) = 'Saturday' then 1 else 0 end)
else 0 end
) as Daysonhire,
from Stock
where itemno = '001127'
group by itemno
MY RESULT IS Daysonhire = 30
AND THE DATES FROM THE DATABASE ARE deliverydate 14/04/2020 collectiondate 16/06/2020
So the result should be 12 or 13 working days
Related
The below code gives a count of 1 for each day, and accumulates from today (13/04/2021) til the end of the month and sums them for Saturdays, Sundays and Week days.
;WITH mycte AS (
SELECT GETDATE() DateValue
UNION ALL
SELECT DateValue + 1
FROM mycte
WHERE DateValue < EOMONTH(dateadd(day,-1, getdate()))
)
select
count(case when datepart(dw, DateValue) = 1 then 1 end) SunCount
, count(case when datepart(dw, DateValue) = 7 then 1 end) SatCount
, count(case when datepart(dw, DateValue) between 1 and 7 then 1 end) WeekCount
from mycte
For today (13/04/2021) I would expect the count to be Saturday = 2, Sunday = 2, and Weekdays to be 14 but instead I get 18 til the end of April - why is that?
I think it is because 'between' includes 1 and 7 again, below query should give you remaining 14 week days
;WITH mycte AS (
SELECT GETDATE() DateValue
UNION ALL
SELECT DateValue + 1
FROM mycte
WHERE DateValue < EOMONTH(dateadd(day,-1, getdate()))
)
select
count(case when datepart(dw, DateValue) = 1 then 1 end) SunCount
, count(case when datepart(dw, DateValue) = 7 then 1 end) SatCount
, count(case when datepart(dw, DateValue) between 2 and 6 then 1 end) WeekCount
from mycte
The below code when run for the last day of the month it is giving me a week day count of 1 when it should be 0 - how can I fix it?
Declare #EndDate DateTime = '03-31-2021'
;WITH mycte AS (
SELECT #EndDate + 1 DateValue
UNION ALL
SELECT DateValue + 1
FROM mycte
WHERE DateValue < EOMONTH(#EndDate)
)
select
count(case when datepart(dw, DateValue) = 1 then 1 end) SunCount
, count(case when datepart(dw, DateValue) = 7 then 1 end) SatCount
, count(case when datepart(dw, DateValue) between 2 and 6 then 1 end) WeekCount
from mycte
Your initial CTE is actually creating a date of 2021-04-01 which is a week day, so that's where your count of 1 is coming from. If you want to restrict the counts to just the month in question, you could add a WHERE clause to your end query like this. This way, you get zeros for all counts.
Declare #EndDate DateTime = '03-31-2021'
;WITH mycte AS (
SELECT #EndDate + 1 DateValue
UNION ALL
SELECT DateValue +1
FROM mycte
WHERE DateValue < EOMONTH(#EndDate)
)
select
count(case when datepart(dw, DateValue) = 1 then 1 end) SunCount
, count(case when datepart(dw, DateValue) = 7 then 1 end) SatCount
, count(case when datepart(dw, DateValue) between 2 and 6 then 1 end) WeekDayCount
from mycte
where DATEPART(MM,DateValue)=DATEPART(MM,#EndDate)
I Have a table which contains information about blockages in a operation for every day of the week in date range. Given that, I need to perform a query which brings only the records that have an active blockage in a given range. E.g.: Suppose there are three registers in the table.
START_DATE END_DATE MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY SUNDAY
2019-01-01 2020-12-31 1 0 0 0 0 0 0
2019-01-31 2019-02-03 0 0 0 0 0 0 1
2018-01-01 2100-12-31 0 0 0 0 0 1 1
And I want to get all the registers which have a blockage between 2019-01-31 and 2019-02-02 (Thursday, Friday and Saturday). The result should bring only the last register.
Is there any way of performing this search entirely in the data base using SQL?
At first, you will have to find intersections of the intervals in the table and the interval you are interested in. Assuming that you define the criteria using two variables #StartDate and #EndDate, it could look like this:
WHERE START_DATE <= #EndDate AND END_DATE >= #StartDate
Then, you will have to find the start and end of the intersection interval:
CASE WHEN START_DATE > #StartDate THEN START_DATE ELSE #StartDate END
CASE WHEN END_DATE < #EndDate THEN END_DATE ELSE #EndDate END
To decide, for example, if that interval contains a Saturday, I count the number of complete weeks between an initial Sunday and the start date and compare that to the respective number for the day after the end date. If both numbers differ, the interval contains a Saturday. My complete suggestion looks like this:
DECLARE #StartDate date = '20190131', #EndDate date = '20190202';
SELECT YourTable.* FROM YourTable
CROSS APPLY( VALUES (
CASE WHEN START_DATE > #StartDate THEN START_DATE ELSE #StartDate END,
CASE WHEN END_DATE < #EndDate THEN END_DATE ELSE #EndDate END)
) Intersection (iStart, iEnd)
CROSS APPLY(
VALUES(DATEDIFF(day, 0, iStart), 1 + DATEDIFF(day, 0, iEnd))
) DaysCount (s, e)
WHERE START_DATE <= #EndDate AND END_DATE >= #StartDate
AND (
(e/7 > s/7 AND SUNDAY = 1)
OR ((e+1)/7 > (s+1)/7 AND SATURDAY = 1)
OR ((e+2)/7 > (s+2)/7 AND FRIDAY = 1)
OR ((e+3)/7 > (s+3)/7 AND THURSDAY = 1)
OR ((e+4)/7 > (s+4)/7 AND WEDNESDAY = 1)
OR ((e+5)/7 > (s+5)/7 AND TUESDAY = 1)
OR ((e+6)/7 > (s+6)/7 AND MONDAY = 1)
);
Remark:
For the counting of days, I use the initial “date 0”, which is 1900-01-01 (a Monday) in SQL Server.
You need to generate all the days and then look for any blockages by day of week:
with days as (
select convert(date, '2019-01-31') as dte
union all
select dateadd(day, 1, dte)
from dte
where dte < '2019-02-02'
)
select
from blockages b cross join
days d
where d.dte >= b.start_date and d.dte <= b.end_date and
( (datename(weekday, d.date) = 'Monday' and Monday = 1) or
(datename(weekday, d.date) = 'Tuesday' and Tuesday = 1) or
(datename(weekday, d.date) = 'Wednesday' and Wednesday = 1) or
(datename(weekday, d.date) = 'Thursday' and Thursday = 1) or
(datename(weekday, d.date) = 'Friday' and Friday = 1) or
(datename(weekday, d.date) = 'Saturday' and Saturday = 1) or
(datename(weekday, d.date) = 'Sunday' and Sunday = 1)
);
Note that if you have more than 99 days in your range, you'll need to include option (maxrecursion 0) to generate the days. Alternatively, you can use a calendar table or tally table.
I want to calculate which of my holidays are conflict with working days.
I have a Holidays table which is below.
NameOfHoliday StartDate DurationByDay
Christmas 26.12.2015 5
26 and 27 are at weekend. So this shouldt be calculated. So I have to get only 4 days as result.
DECLARE #t TABLE(
NameOfHoliday VARCHAR(10),
StartDate DATE,
DurationByDay SMALLINT
)
INSERT INTO #t
VALUES ('Christmas', '20151226', 5)
;WITH cte AS
(
SELECT *, cnt = 0
FROM #t
UNION ALL
SELECT t2.NameOfHoliday, DATEADD(DAY, t1.cnt + 1, t2.StartDate), t2.DurationByDay, t1.cnt + 1
FROM cte t1
JOIN #t t2 ON t1.NameOfHoliday = t2.NameOfHoliday
WHERE t1.cnt < t1.DurationByDay
)
SELECT NameOfHoliday, StartDate, DT
FROM (
SELECT *
, DT = DATENAME(DW, StartDate)
, RowNum = ROW_NUMBER() OVER (PARTITION BY StartDate ORDER BY NameOfHoliday)
FROM cte
) t
WHERE RowNum = 1
AND DT NOT IN ('Saturday', 'Sunday')
OPTION (MAXRECURSION 0)
output -
NameOfHoliday StartDate DT
------------- ---------- ------------------
Christmas 2015-12-28 Monday
Christmas 2015-12-29 Tuesday
Christmas 2015-12-30 Wednesday
Christmas 2015-12-31 Thursday
I Hope your requirement is like this..
if object_id('tempdb..#Holidays') is not null drop table #Holidays
create table #Holidays(id int identity(1,1),Holiday date)
insert into #Holidays values ('2015-12-25'),('2015-12-28')
set dateformat ymd
declare #StartDate date = '2015-12-01'
declare #EndtDate date = '2015-12-31'
--some times #EndtDate might be NULL so, set to getdate()
set #EndtDate = isnull(#EndtDate,cast(getdate() as date))
declare #Holiday int
set #Holiday = (select count(*) from #Holidays where Holiday between #StartDate and #EndtDate)
SELECT
(DATEDIFF(dd, #StartDate, #EndtDate) + 1)
-(DATEDIFF(wk, #StartDate, #EndtDate) * 2)
-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, #EndtDate) = 'Saturday' THEN 1 ELSE 0 END)
-#Holiday
Problem: Display in columns the number of weeks in each month between two date periods (out to three months is fine for now). If possible, from the current day (Dynamic)
Where I currently am:
SELECT Q3.[Begin Date]
,Q3.[End Date]
,Q3.Diff_in_Year
,sum(CASE
WHEN Q3.Year_Counter = 0
THEN datediff(mm, Q3.y_start, Q3.y_end) + 1
ELSE 0
END) Y1
,sum(CASE
WHEN Q3.Year_Counter = 1
THEN datediff(mm, Q3.y_start, Q3.y_end) + 1
ELSE 0
END) Y2
,sum(CASE
WHEN Q3.Year_Counter = 2
THEN datediff(mm, Q3.y_start, Q3.y_end) + 1
ELSE 0
END) Y3
,sum(CASE
WHEN Q3.Year_Counter = 3
THEN datediff(mm, Q3.y_start, Q3.y_end) + 1
ELSE 0
END) Y4
,sum(CASE
WHEN Q3.Year_Counter = 4
THEN datediff(mm, Q3.y_start, Q3.y_end) + 1
ELSE 0
END) Y5
,sum(CASE
WHEN Q3.Year_Counter = 5
THEN datediff(mm, Q3.y_start, Q3.y_end) + 1
ELSE 0
END) Y6
,sum(CASE
WHEN Q3.Year_Counter = 6
THEN datediff(mm, Q3.y_start, Q3.y_end) + 1
ELSE 0
END) Y7
,sum(CASE
WHEN Q3.Year_Counter = 7
THEN datediff(mm, Q3.y_start, Q3.y_end) + 1
ELSE 0
END) Y8
,sum(CASE
WHEN Q3.Year_Counter = 8
THEN datediff(mm, Q3.y_start, Q3.y_end) + 1
ELSE 0
END) Y9
,sum(CASE
WHEN Q3.Year_Counter = 9
THEN datediff(mm, Q3.y_start, Q3.y_end) + 1
ELSE 0
END) Y10
FROM (
SELECT Q1.[Begin Date]
,Q1.[End Date]
,Q1.years Diff_in_Year
,Q2.number AS Year_Counter
,(
CASE
WHEN Q2.number = 0
THEN Q1.[Begin Date]
ELSE dateadd(yy, datediff(yy, 0, dateadd(yy, q2.number, q1.[Begin Date])), 0)
END
) AS y_Start
,(
CASE
WHEN ((Q1.years - 1) = Q2.number)
THEN Q1.[End Date]
ELSE DATEADD(yy, DATEDIFF(yy, 0, dateadd(yy, q2.number + 1, q1.[Begin Date]) + 1), - 1)
END
) AS y_End
,Year(Q1.[Begin Date]) + Q2.number YearInYYYY
FROM (
SELECT [Begin Date]
,[End Date]
,DATEDIFF(year, [Begin Date], [End Date]) + 1 AS years
FROM my dates
) Q1
INNER JOIN master..spt_values Q2 ON Q2.type = 'P'
AND Q2.number < Q1.years
) Q3
GROUP BY Q3.[Begin Date]
,Q3.[End Date]
,q3.Diff_in_Year
How the current code works: Given a date range, the number of months in each year between two dates. IE 1/1/2014 - 1/18/2015 would give two columns "2014" and 2015" the value of 2014 is 12 and the value of 2015 is 1 signifying that there are 13 months between the specified dates.
What I am hoping to achieve is something similar to
Start Date End Date Month 1 Month 2 Month 3
-----------------------------------------------------
1/1/2014 3/8/2014 4 4 1
Dynamic SQL solutions aside (search for dynamic pivot in TSQL), I whipped up a couple of answers. Since your question is unclear whether you want weeks or months, I put together a quick one for each.
Months Example Here:
declare #startdate date = '1/1/2014', #enddate date = '3/1/2015'
select p.*
from (
select #startdate as StartDate, #enddate as EndDate, right(convert(varchar,(dateadd(mm,RowID-1,#startdate)),105),4) [Group]
from (
select *, row_number()over(order by name) as RowID
from master..spt_values
) d
where d.RowID <= datediff(mm, #startdate, #enddate)
) t
pivot (
count([Group]) for [Group] in (
[2014],[2015]
)
) p
Weeks Example Here:
declare #startdate date = '1/1/2014', #enddate date = '3/1/2015'
select p.*
from (
select #startdate as StartDate, #enddate as EndDate, right(convert(varchar,(dateadd(ww,RowID-1,#startdate)),105),7) [Group]
from (
select *, row_number()over(order by name) as RowID
from master..spt_values
) d
where d.RowID <= datediff(ww, #startdate, #enddate)
) t
pivot (
count([Group]) for [Group] in (
[01-2014]
, [02-2014]
, [03-2014]
, [04-2014]
, [05-2014]
, [06-2014]
, [07-2014]
, [08-2014]
, [09-2014]
, [10-2014]
, [11-2014]
, [12-2014]
, [01-2015]
, [02-2015]
, [03-2015]
)
) p