Intersection and consolidation of time periods in SQL

一个人想着一个人 提交于 2020-02-22 15:32:05

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!