I need to calculate how many days one set period contains in another set period. I have table with
create table #test
(
,project nvarchar(10),
startProjectDate date,
endProjectDate date)
insert into #test values
('EE43213','2021-12-31','2022-01-06') ,
('EE0211213','2022-01-09','2022-03-14'),
('EE53134','2022-02-18','2022-02-22')
I have parameters with dates (user input in the future)
DECLARE #startDate DATE = N'2021-12-16'
DECLARE #endDate DATE = N'2022-03-02'
For every project I need to calculate, how many days of their running time will be set on user chosen period and then * this count on some koeff.
I have case when in mind, if the whole project was in parameter-set period, I just find datediff between two project dates and * it.
case when (startProjectDate BETWEEN #startDate and #endDate)
and (endProjectDate BETWEEN #startDate and #endDate)
then DATEDIFF(day, startProjectDate , endProjectDate) + 1 * coeff else ...
But how to find an amount of days if they only partially set on this period?
Seems you just need a DATEDIFF and some CASE expressions here:
DECLARE #StartDate DATE = N'20211216',
#EndDate DATE = N'20220302';
SELECT project,
DATEDIFF(DAY, CASE WHEN startProjectDate < #StartDate THEN #StartDate ELSE startProjectDate END, CASE WHEN endProjectDate > #EndDate THEN #EndDate ELSE endProjectDate END) AS DaysInRange,
DATEDIFF(DAY,startProjectDate, endProjectDate) AS DaysInProject
FROM #test
WHERE startProjectDate <= #EndDate
AND endProjectDate >= #StartDate;
Related
Currently working on a t-sql query which is supposed to get a list of all records that fall between the current fiscal year (we use 4-4-5 calendar).
The record's start and end date are 06/02/2018, 31/07/2020.
The date I am filtering for is:
DECLARE #StartDate DATE = '12-29-2018';
DECLARE #EndDate DATE = '12-31-2019';
The condition I have in my where clause is:
AND
(
(o.Revenue_Start_Date__c >= #StartDate AND o.Revenue_End_Date__c <= #EndDate) OR (o.Revenue_End_Date__c >= #StartDate AND o.Revenue_End_Date__c <= #EndDate)
)
I have tried variations of BETWEEN as well. Any idea what I may be doing wrong and how do I get a list of all records to be included if it falls within the coresponding dates.
ANSWER:
AND ( -- Starts Within range
( o.Revenue_Start_Date__c
BETWEEN #StartDate
AND #EndDate
)
OR -- Ends within range
(
o.Revenue_End_Date__c
BETWEEN #StartDate
AND #EndDate
)
OR -- SPANS Range
(
o.Revenue_Start_Date__c < #StartDate
AND
o.Revenue_End_Date__c > #EndDate
)
)
This seems to be working for me just now.
For fall through cases, this should work.
o.Revenue_Start_Date__c <= #EndDate AND o.Revenue_End_Date__c >= #StartDate
Is this what you want?
(o.Revenue_Start_Date__c < #EndDate AND
o.Revenue_End_Date__c >= #StartDate
)
This is the logic for overlapping intervals. Note that the inequalities might be exact or inexact depending on your actual definitions of overlapping.
i show you my logic is getting error only when month is December
declare #startDate datetime
declare #endDate datetime
set #startDate = convert(varchar(2),#month)+'/1/'+ convert(varchar(4),#year)
set #endDate = dateadd(DD,-1,(convert(varchar(2),#month+1)+'/1/'+convert(varchar(4),#year)))
while(#startDate < #endDate+1)
begin
insert into #tempday
select #startDate
set #startDate = dateadd(day, 1, #startDate )
end
please help
Once you have #startdate, use:
set #enddate = dateadd(day,-1,dateadd(month,1,#startdate))
And I don't think you mean plsql...
There are other things you could think about too, such as not using a while-loop. Why not query a table that has plenty of rows (such as sys.all_columns) and use:
insert #tempday
select top (datediff(day,#startdate,#enddate)+1)
dateadd(day,row_number() over (order by (select 1))-1,#startdate)
from sys.all_columns;
In case of December #month+1 will get you 13 which is not a valid month number
For simplicity lets assume that I have a view with three fields
date_in (date)
Container (varchar)
date_out (date)
Now, the container is IN if the date_in is lesser or equal to given date and date_out is null or greater than given date. Now I am trying to count the containers for given time period. In pseudocode between two values STARTDATE and ENDDATE it would be something like
FOR X =STARTDATE, X<= ENDDADE, X++ {
if date_in <=X and date_out>x
count (container)
}
or closer to SQL:
declare #startdate date,
#d date;
set #startdate = '1/01/2014'
set #d = #startdate
"FOR on the #d variable would go here" {
select #d as SNAP_DATE, count (container) where date_in <#d
and (date_out is null or date_out> #d)
}
It might be simple - I guess I could make a new table and manually do multiple SELECT INTO (and later query from this new table) but its not very elegant solution.
Edit: just to precise - in the end I'd like to have something like:
DATE Count
1/02/2014 10
2/02/2014 15
...
7/03/2014 19
You could do this procedurally as follows:
Use a while loop to loop from start to end date.
Use a table variable to store each date-count pair.
Select from the table variable to get the summarised result.
declare #start date = '1/01/2014'
declare #end date = '7/03/2014'
declare #tbl table(Date date, Count int)
while(#start < #end)
begin
insert into #tbl
select #start, count(*)
from your_view
where (in_date < #start)
and ((out_date is null) or (out_date > #start))
set #start = dateadd(day, 1, #start)
end
select * from #tbl
You might be able to do something like the following. It uses a numbers table, which can be a real or derived table. It contains rows of integers. You need a table that begins with 0 and has enough values to cover your date range. Check here for more information on a numbers table.
DECLARE #StartDate DATE = '1/2/2014'
DECLARE #EndDate DATE = '1/4/2014'
SELECT DATEADD(d, n.num, #StartDate) AS DATE, COUNT(*) AS COUNT
FROM Numbers n
JOIN MyView mv ON mv.date_in < DATEADD(d, n.num, #StartDate)
AND (mv.date_out IS NULL OR mv.date_out > DATEADD(d, n.num, #StartDate))
WHERE DATEADD(d, n.num, #StartDate) BETWEEN #StartDate AND #EndDate
GROUP BY DATEADD(d, n.num, #StartDate)
ORDER BY DATEADD(d, n.num, #StartDate)
The numbers in the numbers table are converted to the list of dates between the date range. Each date is joined to your view based on the criteria you need.
I want number of working days in between to dates. For example if we have 01-01-2012 and 20-01-2012, i want to get the number of working days in between that two dates using T-SQL.
Since SQL Server has no idea what your company considers working days, the best answer to this problem is likely going to be to use a calendar table. Once you have a table with past and future dates, with a column like IsWorkDay correctly updated, the query is simple:
SELECT [Date] FROM dbo.Calendar
WHERE [Date] >= #start
AND [Date] <= #end
AND IsWorkDay = 1;
DECLARE #fromDate datetime, #toDate datetime
SELECT #fromDate = ' 01-01-2012', #toDate = '20-01-2012'
SELECT (DATEDIFF(day, #fromDate, #toDate) + 1)
- (DATEDIFF(week, #fromDate, #toDate) * 2)
- (CASE WHEN DATENAME(weekday, #fromDate) = 'Sunday' THEN 1 ELSE 0 END)
- (CASE WHEN DATENAME(weekday, #toDate) = 'Saturday' THEN 1 ELSE 0 END)
I liked Aaron Bertrand's suggestion so I wrote this code that can be added to your queries. It creates a table variable between 2 dates that you can then use in your query by joining on the CalendarDate column (just remember to strip out any time information before joining). This is based on the typical American work week of Monday through Friday.
DECLARE #StartDate DATE
DECLARE #EndDate DATE
SET #StartDate = '2013-08-19'
SET #EndDate = '2013-08-26'
DECLARE #BusinessDay TABLE
(
CalendarDate DATETIME,
IsBusinessDay INT
)
DECLARE #Counter DATETIME = #StartDate
WHILE(#Counter <= #EndDate)
BEGIN
INSERT INTO #WorkDays
SELECT #Counter, CASE WHEN DATENAME(WEEKDAY, #Counter) NOT IN ('Saturday', 'Sunday') THEN 1 ELSE 0 END
SET #Counter = DATEADD(DAY, 1, #Counter)
END
SELECT * FROM #BusinessDay
The downside is this has to be recreated for each query that needs it, so if you're doing this often, a fixed table might be a better way to go.
It can be used like this....
SELECT
BusinessDays = SUM(IsBusinessDay)
FROM
#BusinessDay
WHERE
CalendarDate BETWEEN #StartDate AND #EndDate
That will give you the count of business days between the two dates. Like many others have said, this obviously does not take into account any holidays or my birthday.
Based on previous code, I adapted it to exclude the last day (not asked but I needed it).
select (DATEDIFF(dd,#fromDate, #toDate))
- (DATEDIFF(ww,#fromDate, DATEADD(dd,-1,#toDate)) * 2)
- (CASE WHEN DATENAME(dw, #fromDate) = 'Sunday' THEN 1 else 0 end)
- (CASE WHEN DATENAME(dw, #toDate) = 'Sunday' THEN 1 else 0 end)
I removed the holydays by using a table containing those dates
- ( select count(distinct dcsdte)
from calendar_table
where dcsdte between #fromDate
and #toDate )
What is a way to find gaps in a set of date spans?
For example, I have these date spans:
1/ 1/11 - 1/10/11
1/13/11 - 1/15/11
1/20/11 - 1/30/11
Then I have a start and end date of 1/7/11 and 1/14/11.
I want to be able to tell that between 1/10/11 and 1/13/11 there is a gap so the start and end date is not possible. Or I want to return only the datespans up to the first gap encountered.
If this can be done in SQL server that would be good.
I was thinking to go through each date to find out if it lands in a datespan... if it does not then there's a gap on that day.
Jump to 2nd last code block for: *I want to be able to tell that
between 1/10/11 and 1/13/11 there is
a gap so the start and end date is*
not possible.
Jump to last code block for: *I want to return only
the datespans up to the first gap
encountered.*
First of all, here's a virtual table to discuss
create table spans (date1 datetime, date2 datetime);
insert into spans select '20110101', '20110110';
insert into spans select '20110113', '20110115';
insert into spans select '20110120', '20110130';
This is a query that will list, individually, all the dates in the calendar
declare #startdate datetime, #enddate datetime
select #startdate = '20110107', #enddate = '20110114'
select distinct a.date1+v.number
from spans A
inner join master..spt_values v
on v.type='P' and v.number between 0 and datediff(d, a.date1, a.date2)
-- we don't care about spans that don't intersect with our range
where A.date1 <= #enddate
and #startdate <= A.date2
Armed with this query, we can now test to see if there are any gaps, by
counting the days in the calendar against the expected number of days
declare #startdate datetime, #enddate datetime
select #startdate = '20110107', #enddate = '20110114'
select case when count(distinct a.date1+v.number)
= datediff(d,#startdate, #enddate) + 1
then 'No gaps' else 'Gap' end
from spans A
inner join master..spt_values v
on v.type='P' and v.number between 0 and datediff(d, a.date1, a.date2)
-- we don't care about spans that don't intersect with our range
where A.date1 <= #enddate
and #startdate <= A.date2
-- count only those dates within our range
and a.date1 + v.number between #startdate and #enddate
Another way to do this is to just build the calendar from #start
to #end up front and look to see if there is a span with this date
declare #startdate datetime, #enddate datetime
select #startdate = '20110107', #enddate = '20110114'
-- startdate+v.number is a day on the calendar
select #startdate + v.number
from master..spt_values v
where v.type='P' and v.number between 0
and datediff(d, #startdate, #enddate)
-- run the part above this line alone to see the calendar
-- the condition checks for dates that are not in any span (gap)
and not exists (
select *
from spans
where #startdate + v.number between date1 and date2)
The query returns ALL dates that are gaps in the date range #start - #end
A TOP 1 can be added to just see if there are gaps
To return all records that are before the gap, use the query as a
derived table in a larger query
declare #startdate datetime, #enddate datetime
select #startdate = '20110107', #enddate = '20110114'
select *
from spans
where date1 <= #enddate and #startdate <= date2 -- overlaps
and date2 < ( -- before the gap
select top 1 #startdate + v.number
from master..spt_values v
where v.type='P' and v.number between 0
and datediff(d, #startdate, #enddate)
and not exists (
select *
from spans
where #startdate + v.number between date1 and date2)
order by 1 ASC
)
Assuming MySQL, something like this would work:
select #olddate := null;
select start_date, end_date, datediff(end_date, #olddate) as diff, #olddate:=enddate
from table
order by start_date asc, end_date asc
having diff > 1;
Basically: cache the previous row's end_date in the #olddate variable, and then do a diff on that "old" value with the currel enddate. THe having clause will return only the records where the difference between two rows is greater than a day.
disclaimer: Haven't tested this, but the basic query construct should work.
I want to be able to tell that between
1/10/11 and 1/13/11 there is a gap so
the start and end date is not
possible.
I think you're asking this question: does the data in your table have a gap between the start date and the end date?
I created a one-column table, date_span, and inserted your date spans into it.
You can identify a gap by counting the number of days between start date and end date, and comparing that the the number of rows in date_span for the same range.
select
date '2011-01-14' - date '2011-01-07' + 1 as elapsed_days,
count(*) from date_span
where cal_date between '2011-01-07' and '2011-01-14';
returns
elapsed_days count
-- --
8 6
Since they're not equal, there's a gap in the table "date_span" between 2011-01-07 and 2011-01-14. I'll stop there for now, because I'm really not certain what you're trying to do.