简体   繁体   中英

Calculate max time interval for each day in table

I have a table in a PostgreSQL database that looks like this:

id |    date    | time_begin | time_end
1  | 2019-03-05 | 10:00:00   | 11:00:00
2  | 2019-03-05 | 13:00:00   | 14:30:00
3  | 2019-03-05 | 14:20:00   | 15:00:00
4  | 2019-03-05 | 17:00:00   | 19:00:00
5  | 2019-03-06 | 09:00:00   | 11:00:00
6  | 2019-03-06 | 10:50:00   | 13:00:00
7  | 2019-03-07 | 10:00:00   | 11:00:00
8  | 2019-03-14 | 12:00:00   | 15:30:00
9  | 2019-03-14 | 16:00:00   | 17:00:00
10 | 2019-03-15 | 18:00:00   | 19:00:00
11 | 2019-03-25 | 09:00:00   | 11:00:00
12 | 2019-03-25 | 11:00:00   | 13:00:00
13 | 2019-03-25 | 13:00:00   | 15:00:00
14 | 2019-03-25 | 15:00:00   | 20:00:00
15 | 2019-03-26 | 09:00:00   | 20:00:00
16 | 2019-03-30 | 09:00:00   | 12:00:00
17 | 2019-03-30 | 12:00:00   | 16:00:00
18 | 2019-03-30 | 16:00:00   | 20:00:00

I want to create a function for calculate max time interval (in minutes) in each day from 9:00 to 20:00 and get a new table. For example:

    date    | duration
 2019-03-05 |   120
 2019-03-06 |   420
 2019-03-07 |   540
 2019-03-14 |   180
 2019-03-15 |   540
 2019-03-25 |    0
 2019-03-26 |    0
 2019-03-30 |    0

How to do it? Any ideas?

Try this-

SELECT 
Date, 
MAX(DATEDIFF(mi, time_begin , time_end ) )
FROM your_table
GROUP BY Date

Is this what you want?

select date, max(time_end - time_begin)
from t
group by date
order by date;

If you want the difference in minutes:

select date,
       extract(epoch from max(time_end - time_begin)) / 60
from t
group by date
order by date;

It is not clear that you need to be concerned about your bounds; all the values appear to be within the bounds.

First off, I think you threw everybody off by asking for "max interval" without much more explanation. Each row in your dataset defines an interval, so you ended up with responses showing that.

If I understand correctly, what you're looking for is: "The maximum period of time, per day, which is between 9am and 8pm, and not covered by the periods in the data set". That's what I'll try to get for you.

As it turns out, that's a lot less straightforward than the question people thought you were asking :)

We want to do some calculations between different rows. In SQL, the way to do that is with window functions . In this case, we want to use lead and lag . And we want to partition by date.

In that case, the previous row's time_end would be expressed:

lag(time_end, 1)
  OVER (PARTITION BY date ORDER BY time_begin)

You could order by id, too, if you'd rather, but since we're comparing times, using times for the ordering seems reasonable. Similarly, the next row's time_begin looks like:

lead(time_begin, 1)
  OVER (PARTITION BY date ORDER BY time_begin)

Add those together with a little date math, and the query overall looks like:

SELECT date,
  time_begin - lag(time_end, 1)
                OVER (PARTITION BY date ORDER BY time_begin),
  lead(time_begin, 1) OVER (PARTITION BY date ORDER BY time_begin)
    - time_end
FROM your_table
ORDER BY date;

The result looks something like:

    date    | ?column?  | ?column?  
------------+-----------+-----------
 2019-03-05 |           | 02:00:00
 2019-03-05 | 02:00:00  | -00:10:00
 2019-03-05 | -00:10:00 | 02:00:00
 2019-03-05 | 02:00:00  | 
...

This gets us pretty far. Notably, though, we're missing any intervals between 9am and the first time_begin . Same with the last time_end and 8pm -- those are just showing up as null. Fortunately, lag and lead come with an extra option argument that serves as a default value:

SELECT date,
  time_begin - lag(time_end, 1, '09:00:00')
                OVER (PARTITION BY date ORDER BY time_begin),
  lead(time_begin, 1, '20:00:00') OVER (PARTITION BY date ORDER BY time_begin)
    - time_end
FROM your_table
ORDER BY date;

Results:

    date    | ?column?  | ?column?  
------------+-----------+-----------
 2019-03-05 | 01:00:00  | 02:00:00
 2019-03-05 | 02:00:00  | -00:10:00
 2019-03-05 | -00:10:00 | 02:00:00
 2019-03-05 | 02:00:00  | 01:00:00
...

Okay, now we're getting somewhere. But we need to aggregate that. Let's use common table expressions since we already have a query that works. I'll also add some column aliases for our interval columns:

WITH date_intervals as (
 SELECT date,
    time_begin - lag(time_end, 1, '09:00:00')
                  OVER (PARTITION BY date ORDER BY time_begin) AS interval1,
    lead(time_begin, 1, '20:00:00') OVER (PARTITION BY date ORDER BY time_begin)
      - time_end AS interval2
  FROM your_table
  ORDER BY date)
SELECT date, max(interval1), max(interval2)
FROM date_intervals
GROUP BY date
ORDER BY date;

Now let's use Gordon Linoff's idea to convert to minutes, and the greatest function to get what you want:

WITH date_intervals as (
 SELECT date,
    time_begin - lag(time_end, 1, '09:00:00')
                  OVER (PARTITION BY date ORDER BY time_begin) AS interval1,
    lead(time_begin, 1, '20:00:00') OVER (PARTITION BY date ORDER BY time_begin)
      - time_end AS interval2
  FROM your_table
  ORDER BY date)
SELECT date,
  greatest(
    extract(epoch from max(interval1)) / 60,
    extract(epoch from max(interval2)) / 60)
FROM date_intervals
GROUP BY date
ORDER BY date;

And there you have it. Complicated, but doable in small steps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM