问题
I want to achieve similar function that is available in Time Period Library for .NET, but in SQL.
First, I have a table with several rows with an Start Date and an End Date, and I want to consolidate them together like this:
Then with that result and another coming from a different table, I want to find out the intersection between the two of them, like this but only 2 inputs (find the periods that are present in both):
Once I have the intersection is just summing up the time on it.
Here I provide a SQL Fiddle with the expected output with an example:
http://sqlfiddle.com/#!18/504fa/3
回答1:
Sample data preparation
CREATE TABLE TableToCombine
([IdDoc] int IDENTITY(1,1), [IdEmployee] int, [StartDate] datetime, [EndDate] datetime)
;
INSERT INTO TableToCombine
(IdEmployee, StartDate, EndDate)
VALUES
(1, '2018-01-01 06:00:00', '2018-01-01 14:00:00'),
(2, '2018-01-01 11:00:00', '2018-01-01 19:00:00'),
(3, '2018-01-01 20:00:00', '2018-01-02 03:00:00'),
(1, '2018-01-02 06:00:00', '2018-01-02 14:00:00'),
(2, '2018-01-02 11:00:00', '2018-01-02 19:00:00')
;
CREATE TABLE TableToIntersect
([IdDoc] int IDENTITY(1,1), [OrderId] int, [StartDate] datetime, [EndDate] datetime)
;
INSERT INTO TableToIntersect
(OrderId, StartDate, EndDate)
VALUES
(1, '2018-01-01 09:00:00', '2018-01-02 12:00:00')
;
Query:
with ExpectedCombineOutput as (
select
grp, StartDate = min(StartDate), EndDate = max(EndDate)
from (
select
*, sum(iif(cd between StartDate and EndDate, 0, 1))over(order by StartDate) grp
from (
select
*, lag(EndDate) over (order by IdDoc) cd
from
TableToCombine
) t
) t
group by grp
)
select
a.grp, StartDate = iif(a.StartDate < b.StartDate, b.StartDate, a.StartDate)
, EndDate = iif(a.EndDate < b.EndDate, a.EndDate, b.EndDate)
from
ExpectedCombineOutput a
join TableToIntersect b on a.StartDate <= b.EndDate and a.EndDate >= b.StartDate
Intersecting time intervals are combined in CTE. And then joined with your intersectTable to find overlapping periods. Two periods overlap if a.StartDate < b.EndDate and a.EndDate > b.StartDate
回答2:
PinX0, I am not sure if I have totally understood your question/ requirement. Following query gives you result for ExpectedIntersectOutput:-
Insert into #ExpectedCombineOutput
select distinct min(startdate), max(enddate) from
(
Select tc.IdDoc as IdDoc, tcw.IdDoc as SecIdDoc,
case when (tc.startdate between tcw.startdate and tcw.enddate) then tcw.startdate else tc.StartDate end as StartDate,
case when (tc.enddate between tcw.startdate and tcw.enddate) then tcw.enddate else tc.EndDate end as EndDate
from #TableToCombine tc
left join #TableToCombine tcw on tc.IdDoc <> tcw.IdDoc
) test group by IdDoc
Insert into #ExpectedIntersectOutput
Select case when ti.startdate <= tc.startdate then tc.startdate else ti.startdate end as 'StartDate',
case when ti.enddate >= tc.enddate then tc.enddate else ti.enddate end as 'EndDate'
from #TableToIntersect ti
inner join #ExpectedCombineOutput tc on 1=1
回答3:
here is a working example in MySQL. I was using a simple view for this.
Let us consider a fitness with customers and employees. You need to know how many hours were spent by customers when an employee was present.
First let you prepare a testing table:
CREATE TABLE Customer
(id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
customerId int NOT NULL,
arrival datetime,
leaving datetime);
INSERT INTO Customer
(customerId, arrival, leaving)
VALUES
(1, '2018-01-01 06:00:00', '2018-01-01 14:00:00'),
(2, '2018-01-01 11:00:00', '2018-01-01 19:00:00'),
(3, '2018-01-01 20:00:00', '2018-01-02 03:00:00'),
(1, '2018-01-02 06:00:00', '2018-01-02 14:00:00'),
(2, '2018-01-02 11:00:00', '2018-01-02 19:00:00')
;
CREATE TABLE Employee
(id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
employeeId int NOT NULL,
arrival datetime,
leaving datetime)
;
INSERT INTO Employee
(employeeId, arrival, leaving)
VALUES
(1, '2018-01-01 09:00:00', '2018-01-01 12:00:00',),
(2, '2018-01-01 11:30:00', '2018-01-01 20:00:00')
;
When you have a table, let you create a view with time intersections:
CREATE OR REPLACE VIEW intersectionTimeView AS select e.employeeId,
c.customerId,
IF(c.arrival>e.arrival, c.arrival, e.arrival) AS arrivalMax,
IF(c.leaving>e.leaving, e.leaving, c.leaving) AS leavingMin
FROM Customer c, Employee e
WHERE TIMEDIFF(c.arrival,e.leaving)<=0
AND TIMEDIFF(c.leaving,e.arrival)>=0
And finally and easily you can get the hours by:
SELECT employeeId, SUM( timestampdiff(minute,arrivalMax,leavingMin)/60) as summ
FROM intersectionTimeView WHERE employeeId=2
GROUP BY employeeId
来源:https://stackoverflow.com/questions/48578248/intersection-and-consolidation-of-time-periods-in-sql