在postgres中找到免费的相邻时段

时间:2013-09-04 12:46:41

标签: sql postgresql

我有一个包含用于预约约会的时间段的表格,我试图找出一种带有sql语句/视图的方法来查找不同持续时间约会的相邻免费时段。

create table如下所示:

CREATE TABLE timeslot
(
  timeslot_id bigserial NOT NULL,
  duration bigint,
  successor bigint,
  predecessor bigint,
  start_year character varying NOT NULL,
  start_month character varying NOT NULL,
  start_day character varying NOT NULL,
  start_hour character varying NOT NULL,
  start_minute character varying NOT NULL,
  end_year character varying NOT NULL,
  end_month character varying NOT NULL,
  end_day character varying NOT NULL,
  end_hour character varying NOT NULL,
  end_minute character varying NOT NULL,
  employee_id integer NOT NULL,
  available_status_id integer,
  appoint_calendar_id integer
  CONSTRAINT timeslot_id PRIMARY KEY (timeslot_id),
  CONSTRAINT appoint_calendar_id FOREIGN KEY (appoint_calendar_id)
  REFERENCES appoint_calendar (appoint_calendar_id) MATCH SIMPLE
  ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT available_status_id FOREIGN KEY (available_status_id)
  REFERENCES available_status (available_status_id) MATCH SIMPLE
  ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT employee_id FOREIGN KEY (employee_id)
  REFERENCES employee (employee_id) MATCH SIMPLE
  ON UPDATE NO ACTION ON DELETE NO ACTION
)

以下是插入数据的示例,其中available_status_id为1表示空闲时间段 而available_status_id为2是一个空闲时间段:

INSERT INTO timeslot(
        timeslot_id, duration, successor, predecessor, start_year, start_month, 
        start_day, start_hour, start_minute, end_year, end_month, end_day, 
        end_hour, end_minute, employee_id, available_status_id, appoint_calendar_id)
VALUES (11870, 30, null, 11869, "2013", "09", 
        "02", "18", "00", "2013", "09", "02", 
        "18", "30", 4, 1, null);

INSERT INTO timeslot(
        timeslot_id, duration, successor, predecessor, start_year, start_month, 
        start_day, start_hour, start_minute, end_year, end_month, end_day, 
        end_hour, end_minute, employee_id, available_status_id, appoint_calendar_id)
VALUES (11904, 30, 12000, 11999, "2013", "09", 
        "09", "10", "30", "2013", "09", "09", 
        "11", "00", 5, 2, 761);

我正在寻找postgres中的查询,以查找不同持续时间(如15,30或60分钟)的约会的所有免费时间段。目前我只是从数据库中获取所有空闲时间段并在Java中迭代它们并将持续时间分钟加在一起,直到找到足够的相邻时隙并返回每个子组的第一个时隙以显示在日历中。但是postgres必须有更好更快的方式吗? 提前致谢

修改

输入是所需的持续时间(以分钟为单位)(例如60),employee_id(例如5)和日期(例如09.09.2013)。 所需的输出是相邻(在时间上),空闲且具有足够持续时间的所有子集。 对于上面的例子,这可能是:

timeslot_id 11904
duration 30
successor 12000
predecessor 11999
start_year 2013
start_month 09
start_day 09
start_hour 10
start_minute 30
end_year 2013
end_month 09
end_day 09
end_hour 11
end_minute 00 
employee_id 5
available_status_id 1
appoint_calendar_id null

timeslot_id 12000
duration 30
successor 11906
predecessor 11904
start_year 2013
start_month 09
start_day 09
start_hour 11
start_minute 00
end_year 2013
end_month 09
end_day 09
end_hour 11
end_minute 30
employee_id 5
available_status_id 1
appoint_calendar_id null

2 个答案:

答案 0 :(得分:1)

就个人而言,我认为用Java代码制作它是个好主意。

其他选项可能是使用游标创建PostgreSQL函数。

但是如果你真的想在一个SQL请求中执行此操作,并假设你的每个连续时间段的时间段恰好相差一个,你可以预测最长的约会持续时间和所需的时间段你可以试试这样的事情:

select ts1.timeslot_id as start_timeslot_id, 
    coalesce(t4.timeslot_id, t3.timeslot_id, t2.timeslot_id, t1.timeslot_id) as end_timeslot_id, 
    coalesce(t4.end_hour, t3.end_hour, t2.end_hour, t1.end_hour)*60+coalesce(t4.end_minute, t3.end_minute, t2.end_minute, t1.end_minute) - t1.start_hour*60+t1.start_minute as duration_minutes
from timeslot ts1
left join timeslot ts2 
on ts1.timeslot_id+1 = ts2.timeslot_id
and t12.available_status_id = 1 
left join timeslot ts3 
on ts2.timeslot_id+1 = ts3.timeslot_id
and ts3.available_status_id = 1 
left join timeslot ts4 
on ts3.timeslot_id+1 = ts4.timeslot_id
and ts4.available_status_id = 1 
where ts1.start_year = '2013' -- these all are your input parameters
and ts1.start_month = '09'
and ts1.start_day = '09'
and employee_id = 5
and coalesce(t4.end_hour, t3.end_hour, t2.end_hour, t1.end_hour)*60+coalesce(t4.end_minute, t3.end_minute, t2.end_minute, t1.end_minute) - t1.start_hour*60+t1.start_minute >= 60 -- duration in minutes

select ts1.timeslot_id as start_timeslot_id, coalesce(t4.timeslot_id, t3.timeslot_id, t2.timeslot_id, t1.timeslot_id) as end_timeslot_id, coalesce(t4.end_hour, t3.end_hour, t2.end_hour, t1.end_hour)*60+coalesce(t4.end_minute, t3.end_minute, t2.end_minute, t1.end_minute) - t1.start_hour*60+t1.start_minute as duration_minutes from timeslot ts1 left join timeslot ts2 on ts1.timeslot_id+1 = ts2.timeslot_id and t12.available_status_id = 1 left join timeslot ts3 on ts2.timeslot_id+1 = ts3.timeslot_id and ts3.available_status_id = 1 left join timeslot ts4 on ts3.timeslot_id+1 = ts4.timeslot_id and ts4.available_status_id = 1 where ts1.start_year = '2013' -- these all are your input parameters and ts1.start_month = '09' and ts1.start_day = '09' and employee_id = 5 and coalesce(t4.end_hour, t3.end_hour, t2.end_hour, t1.end_hour)*60+coalesce(t4.end_minute, t3.end_minute, t2.end_minute, t1.end_minute) - t1.start_hour*60+t1.start_minute >= 60 -- duration in minutes

据推测,此请求将为您提供每个可能的时间段,这些时间段大于或等于所需的时间段。 我没有尝试对真实数据库运行此查询,因此它可能包含错误。

答案 1 :(得分:0)

CREATE TABLE appoint_calendar ( appoint_calendar_id SERIAL NOT NULL PRIMARY KEY);
INSERT INTO appoint_calendar(appoint_calendar_id) VALUES (761),(762);

CREATE TABLE employee (employee_id SERIAL NOT NULL PRIMARY KEY);
INSERT INTO employee(employee_id) VALUES (4),(5);

CREATE TABLE available_status (available_status_id SERIAL NOT NULL PRIMARY KEY);
INSERT INTO available_status(available_status_id) VALUES (1),(2);


CREATE TABLE timeslot
( timeslot_id bigserial NOT NULL PRIMARY KEY
  , duration bigint
  , successor bigint
  , predecessor bigint
  , start_date timestamp with time zone
  , end_date timestamp with time zone
  , employee_id integer NOT NULL
        REFERENCES employee (employee_id) MATCH SIMPLE
        ON UPDATE NO ACTION ON DELETE NO ACTION
  , available_status_id integer
        REFERENCES available_status (available_status_id) MATCH SIMPLE
        ON UPDATE NO ACTION ON DELETE NO ACTION
  , appoint_calendar_id integer
        REFERENCES appoint_calendar (appoint_calendar_id) MATCH SIMPLE
        ON UPDATE NO ACTION ON DELETE NO ACTION
);

INSERT INTO timeslot(timeslot_id, duration,successor,predecessor,start_date,end_date,employee_id,available_status_id,appoint_calendar_id) VALUES
    (11870, 30, null, 11869, '2013-09-02 18:00:00', '2013-09-02 18:30:00', 4, 1, null)
  , (11904, 30, 12000, 11999, '2013-09-09 10:30:00', '2013-09-09 11:00:00', 5, 2, 761)
        ;

选择一些时隙(区间运算不完全正确,YMMV)

SELECT * FROM timeslot ts
WHERE ts.employee_id IN(5)
AND ts.available_status_id IN(1, 2)
AND ts.start_date::date = '2013-09-09'::date
AND ts.end_date >= (ts.start_date + '30 min'::interval)
        ;