postgresql 从多个日期范围提取每周的天数

3qpi33ja  于 2023-01-13  发布在  PostgreSQL
关注(0)|答案(2)|浏览(118)

我在PostgreSQL 10.5中有一个表trips

id  start_date    end_date
----------------------------
1   02/01/2019    02/03/2019
2   02/02/2019    02/03/2019
3   02/06/2019    02/07/2019
4   02/06/2019    02/14/2019
5   02/06/2019    02/06/2019

我要计算行程中与给定周重叠的天数。表中的行程具有包含界限。周从星期一开始,星期日结束。预期结果为:

week_of    days_utilized
------------------------
01/28/19    5
02/04/19    8
02/11/19    4

对于日历参考:

Monday 01/28/19 - Sunday 02/03/19
Monday 02/04/19 - Sunday 02/10/19
Monday 02/11/19 - Sunday 02/17/19

我知道如何用我使用的编程语言来写这个,但是我更喜欢用Postgres来写,我不清楚从哪里开始...

qeeaahzv

qeeaahzv1#

您似乎想要generate_series()joingroup by。要计算涵盖的周数:

select gs.wk, count(t.id) as num_trips
from generate_series('2019-01-28'::date, '2019-02-11'::date, interval '1 week') gs(wk) left join
     trips t
     on gs.wk <= t.end_date and
        gs.wk + interval '6 day' >= t.start_date
group by gs.wk
order by gs.wk;

编辑:
我知道你想把日子都安排好了。这是一个稍微多一点的工作:

select gs.wk, count(t.id) as num_trips,
       sum( 1 +
            extract(day from (least(gs.wk + interval '6 day', t.end_date) - greatest(gs.wk, t.start_date)))
          ) as days_utilized
from generate_series('2019-01-28'::date, '2019-02-11'::date, interval '1 week') gs(wk) left join
     trips t
     on gs.wk <= t.end_date and
        gs.wk + interval '6 day' >= t.start_date
group by gs.wk
order by gs.wk;

注意:这并没有返回你得到的确切结果。我认为这些是正确的。

rseugnpd

rseugnpd2#

考虑一下range types。使用范围运算符可以使计算更简单更清晰。我使用了下面的重叠运算符&&和交集运算符*。如果表很大,可以使用函数式GiST或SP-GiST索引来支持它,以使查询更快。例如:

CREATE INDEX trip_range_idx ON trip
USING gist (daterange(start_date, end_date, '[]'));

然后,您的查询可以使用此索引:

SELECT week
     , count(overlap)                       AS ct_trips
     , sum(upper(overlap) - lower(overlap)) AS days_utilized
FROM  (
   SELECT week, trip * week AS overlap
   FROM  (
      SELECT daterange(mon::date, mon::date + 7) AS week
      FROM   generate_series(timestamp '2019-01-28'
                           , timestamp '2019-02-11'
                           , interval  '1 week') mon
      ) w
   LEFT   JOIN (SELECT daterange(start_date, end_date, '[]') FROM trip) t(trip) ON trip && week
   ) sub
GROUP  BY 1
ORDER  BY 1;
  • db〈〉小提琴here *

默认情况下,date_range包含一个 * 包含 * 下限和一个 * 不包含 * 上限。* 您的 * 范围 * 包含 * 上限和下限,因此使用以下内容创建daterangedaterange(start_date, end_date, '[]')。函数upper()仍然返回互斥上限。因此表达式upper(overlap) - lower(overlap)正确地计算了天数。
我将generate_series()timestamp输入一起使用是有原因的:

  • 在PostgreSQL中生成两个日期之间的时间序列

相关:

  • 在PostgreSQL中执行此小时的操作查询
    • 或者**,如果不想使用范围类型,请考虑OVERLAPS运算符:
  • 在PostgreSQL中查找重叠的日期范围

相关问题