5

从如何在 PostgreSql 的预订表中找到第一个空闲时间的好答案中选择

create table reservation (during tsrange,
 EXCLUDE USING gist (during WITH &&)
 );

用于查找从给定日期和时间开始的日程安排间隙(2012-11-17 8:在下面的示例中)它还可以找到星期六、星期日和公共假期。公众假期定义见表

create table pyha ( pyha date primary key)

如何也排除周末和公共假期?

硬编码空闲时间保留为查询

with gaps as (
  select
    upper(during) as start,
    lead(lower(during),1,upper(during)) over (ORDER BY during) - upper(during) as gap
  from (
    select during
    from reservation
   union all values
     ('(,2012-11-17 8:)'::tsrange), -- given date and hour from which to find free work time
     ('[2012-11-17 0:,2012-11-18 24:)'::tsrange), -- exclude saturday
     ('[2012-11-18 0:,2012-11-19 8:)'::tsrange),  -- exclude sunday
     ('[2012-11-19 18:,2012-11-20 8:)'::tsrange),
     ('[2012-11-20 18:,2012-11-21 8:)'::tsrange),
     ('[2012-11-21 18:,2012-11-22 8:)'::tsrange),
     ('[2012-11-22 18:,2012-11-23 8:)'::tsrange),
     ('[2012-11-23 18:,2012-11-24 24:)'::tsrange),
     ('[2012-11-24 0:,2012-11-25 24:)'::tsrange), -- exclude saturday
     ('[2012-11-25 0:,2012-11-26 8:)'::tsrange)  -- exclude sunday
 ) as x
)
select *
  from gaps
where gap > '0'::interval
order by start

每个空闲时间范围都需要单独的联合行。

从给定日期和时间开始,在工作日和工作时间(8:00 .. 18:00)中返回空闲时间的最佳方式是什么?

更新

Select in answer 总是在 8:00 返回空闲时间。如何在指定的开始日期返回不早于指定开始时间的空闲时间,例如如果开始时间为 9,则不早于 2012-11-19 9:00?开始时间可能只有值 8,9,10,11,12,13,14,15,16 或 17

即使 2012-11-19 8:00 如果免费,它也应该返回 2012-11-19 9:00。仅当 2012-11-19 9:00 没有空闲时间且后续工作日 8:00 首次空闲时,才应返回 8:00。

我尝试通过将2012-11-19 9:添加到两个位置来解决此问题,如下面的查询所示,但此查询仍返回 2012-11-19 8:00 的空闲时间。如何解决此问题,使其在 2012-11-19 9:00 返回空闲时间?

create table reservation (during tsrange,
 EXCLUDE USING gist (during WITH &&)
 );
create table pyha ( pyha date primary key);
with gaps as (
    select
        upper(during) as start,
        lead(lower(during),1,upper(during)) over (ORDER BY during) - upper(during) as gap
    from (
        select during
          from reservation
             where upper(during)>= '2012-11-19 9:'
       union all values
         ('(,2012-11-19 9:)'::tsrange)
        union all
        select
            unnest(case
                when pyha is not null then array[tsrange(d, d + interval '1 day')]
                when date_part('dow', d) in (0, 6) then array[tsrange(d, d + interval '1 day')]
                else array[tsrange(d, d + interval '8 hours'),
                           tsrange(d + interval '18 hours', d + interval '1 day')]
            end)
        from generate_series(
            '2012-11-19'::timestamp without time zone,
            '2012-11-19'::timestamp without time zone+ interval '3 month',
            interval '1 day'
        ) as s(d)
        left join pyha on pyha = d::date
    ) as x
)

select start,
   date_part('epoch', gap) / (60*60) as hours
  from gaps
where gap > '0'::interval
order by start

更新2

我尝试了更新的答案,但它返回了错误的数据。完整的测试用例是:

create temp table reservation  ( during tsrange ) on commit drop;
insert into reservation values(
'[2012-11-19 11:00:00,2012-11-19 11:30:00)'::tsrange );

with gaps as (
    select
        upper(during) as start,
        lead(lower(during),1,upper(during)) over (ORDER BY during) - upper(during) as gap
    from (
        select during
          from reservation
        union all
        select
            unnest(case
                when pyha is not null then array[tsrange(d, d + interval '1 day')]
                when date_part('dow', d) in (0, 6) then array[tsrange(d, d + interval '1 day')]
                when d::date =  DATE'2012-11-19' then array[
                            tsrange(d, '2012-11-19 12:'),  -- must return starting at 12:00
                            tsrange(d + interval '18 hours', d + interval '1 day')]
                else array[tsrange(d, d + interval '8 hours'), 
                           tsrange(d + interval '18 hours', d + interval '1 day')]
            end)
        from generate_series(
            DATE'2012-11-19'::timestamp without time zone,
            DATE'2012-11-19'::timestamp without time zone+ interval '3 month',
            interval '1 day'
        ) as s(d) 
        left join pyha on pyha = d::date
    ) as x 
)

select start,
   date_part('epoch', gap) / (60*60) as tunde
  from gaps 
where gap > '0'::interval
order by start

观察到的第一行:

"2012-11-19 11:30:00"

预期的 :

"2012-11-19 12:00:00"

怎么修 ?

4

1 回答 1

2

您可以使用 generate_series() 函数来屏蔽非营业时间:

with gaps as (
    select
        upper(during) as start,
        lead(lower(during),1,upper(during)) over (ORDER BY during) - upper(during) as gap
    from (
        select during
        from reservation
        union all
        select
            unnest(case
                when pyha is not null then array[tsrange(d, d + interval '1 day')]
                when date_part('dow', d) in (0, 6) then array[tsrange(d, d + interval '1 day')]
                when d::date = '2012-11-14' then array[tsrange(d, d + interval '9 hours'), tsrange(d + interval '18 hours', d + interval '1 day')]
                else array[tsrange(d, d + interval '8 hours'), tsrange(d + interval '18 hours', d + interval '1 day')]
            end)
        from generate_series(
            '2012-11-14'::timestamp without time zone, 
            '2012-11-14'::timestamp without time zone + interval '2 week', 
            interval '1 day'
        ) as s(d) 
        left join pyha on pyha = d::date
    ) as x 
)
select *
    from gaps
where gap > '0'::interval
order by start

让我解释一些棘手的部分:

  • 您不必将周六/周日的日期插入pyha表中,因为您可以使用date_part('dow', d)函数。仅在公共假期使用pyha表格。'dow' 分别为 Sun 或 Sat 返回 0 或 6。
  • 公共假期和周六/周日可以表示为单个间隔 (0..24)。工作日必须由两个间隔 (0..8) 和 (18..24) 表示,因此 unnest() 和 array[]
  • 您可以在 generate_series() 函数中指定开始日期和长度

根据您对问题的更新,我添加了when另一个case

when d::date = '2012-11-14' then array[tsrange(d, d + interval '9 hours'), tsrange(d + interval '18 hours', d + interval '1 day')]

这个想法是为开始日期(d::date = '2012-11-14')产生不同的间隔:(0..9)和(18..24)

于 2012-11-18T02:59:09.070 回答