2012-11-25 44 views
2

的PostgreSQL 9.2预约表被定义为只允许工作时间预约表

CREATE EXTENSION btree_gist; 
CREATE TABLE schedule (
    id serial primary key, 
    during tsrange not null, 
    EXCLUDE USING gist (during WITH &&) 
); 

假期在表中列出

CREATE TABLE holiday (day primary key); 

工作时间为8至18:00在工作日和reservatons只能以30分钟的时间间隔完成。 如何在值制约添加,使其在工作时间只允许保留:在tsrange

  1. 开始和结束日期总是相同的。
  2. 开始和结束日期不能是星期六和星期日
  3. 开始和结束日期不能出现在公众假期表
  4. 开始时间只能是8:00,8:30,9:00,9:30,。 16:00,16:30,17:00,17:30包括:
  5. 结束时间可以只有8:30,9:00,9:30,... 16:00,16:30,17 :00,17:30或18:00独家

将这些或那些约束添加到此表中是否合理? 如果是,如何添加?如果这有所帮助,则可以更改日程表表格结构 。

+1

其中一些可以用简单的'CHECK'约束完成。我不确定你可以直接为公众假期实施负外键约束;考虑为此使用触发器。 –

+0

我是新来的tsrange类型。你能举一些例子,特别是时间限制 – Andrus

回答

1

您需要更改表的定义并添加一些check约束:

CREATE TABLE schedule (
    id serial primary key, 
    during tsrange not null check(
    (lower(during)::date = upper(during)::date) and 
    (date_trunc('hour', upper(during)) + INTERVAL '30 min' * ROUND(date_part('minute', upper(during))/30.0) = upper(during)) and 
    (date_trunc('hour', lower(during)) + INTERVAL '30 min' * ROUND(date_part('minute', lower(during))/30.0) = lower(during)) and 
    (lower(during)::time >= '8:00'::time and upper(during)::time <= '18:00'::time) and 
    (date_part('dow', lower(during)) in (1,2,3,4,5) and date_part('dow', upper(during)) in (1,2,3,4,5)) 
), 
    EXCLUDE USING gist (during WITH &&) 
); 

的检查是在这个顺序

  • 开始和结束日期是相同的
  • 启动/末端必须在30分钟的界限上
  • 和8:00 .. 18:00
  • 只有平日

我们需要holiday桌上的东西: 插入节日值( '2012年11月28日');

check不能引用其他表,因此我们需要触发功能(它可能是更好地把所有的检查到这个功能,即让他们在一个地方):

create function holiday_check() returns trigger language plpgsql stable as $$ 
begin 
    if exists (select * from holiday where day in (lower(NEW.during)::date, upper(NEW.during)::date)) then 
     raise exception 'public holiday'; 
    else 
     return NEW; 
    end if; 
end; 
$$; 

然后我们需要insert之前创建触发器/ update

create trigger holiday_check_i before insert on schedule for each row execute procedure holiday_check(); 
create trigger holiday_check_u before update on schedule for each row execute procedure holiday_check(); 

最后,一些测试:

-- OK 
insert into schedule(during) values (tsrange('2012-11-26 08:00', '2012-11-26 09:00')); 
INSERT 0 1 

-- out of business hours 
insert into schedule(during) values (tsrange('2012-11-26 04:00', '2012-11-26 05:00')); 
ERROR: new row for relation "schedule" violates check constraint "schedule_during_check" 
DETAIL: Failing row contains (12, ["2012-11-26 04:00:00","2012-11-26 05:00:00")). 

-- End time can be only 8:30, 9:00, 9:30, ... 16:00, 16:30, 17:00, 17:30 or 18:00 exclusive 
insert into schedule(during) values (tsrange('2012-11-26 08:00', '2012-11-26 09:10')); 
ERROR: new row for relation "schedule" violates check constraint "schedule_during_check" 
DETAIL: Failing row contains (13, ["2012-11-26 08:00:00","2012-11-26 09:10:00")). 

-- Start time can be only 8:00 , 8:30, 9:00, 9:30, ... 16:00, 16:30, 17:00 or 17:30 inclusive 
insert into schedule(during) values (tsrange('2012-11-26 11:24', '2012-11-26 13:00')); 
ERROR: new row for relation "schedule" violates check constraint "schedule_during_check" 
DETAIL: Failing row contains (14, ["2012-11-26 11:24:00","2012-11-26 13:00:00")). 

-- holiday 
insert into schedule(during) values (tsrange('2012-11-28 10:00', '2012-11-28 13:00')); 
ERROR: public holiday 
+0

非常感谢。优秀。顺便说一句。你在http://stackoverflow.com/questions/13433863/how-to-return-only-work-time-from-reservations-in-postgresql看了更新的问题。它看起来像你这个问题的答案返回错误的数据,因为构造select返回重叠范围。 – Andrus

+0

为什么你创建两个触发器?这可以通过在插入或更新...语句之前使用一个'create trigger holiday_check_i来完成?有两个有什么好处? –

4

可以解决,除了第3项都与普通CHECK constraints

使用专用range functionslower(anysrange) and upper(anyrange)访问范围的下/上边界。

1.)tsrange中的开始和结束日期总是相同的。

CONSTRAINT schedule_same_day 
CHECK (lower(during)::date = upper(during)::date) 

2)开始和结束日期不能是星期六和星期日

使用isodow, not dow更简单的表达。

CONSTRAINT schedule_no weekend 
CHECK (EXTRACT(ISODOW FROM lower(during)) < 6) -- upper on same day 

3)开始日期和结束日期不能出现在公众假期表

唯一的例外:这个你需要一个触发,如:

CREATE OR REPLACE FUNCTION trg_during_no_holy() 
    RETURNS trigger AS 
$func$ 
BEGIN 

IF EXISTS (SELECT 1 FROM holiday WHERE day = lower(NEW.during)) THEN 
    RAISE EXCEPTION 'Day too holy: %', lower(NEW.during); 
END IF; 

RETURN NEW; 

END 
$func$ LANGUAGE plpgsql VOLATILE; 

CREATE TRIGGER insupbef_holycheck 
    BEFORE INSERT OR UPDATE 
    ON schedule 
    FOR EACH ROW 
    EXECUTE PROCEDURE trg_during_no_holy(); 

4)开始时间只能是8:00,8:30,9:00,9:30,... 16:00,16:30,17:00或17:30,包括
5.)结束时间只能是8:30,9:00,9:30 ...... 16:00,16:30,17:00,17:30或18:00专享

CONSTRAINT schedule_8_inc_to_18_exc_half_hours 
CHECK (lower(during)::time BETWEEN time '8:00'AND time '17:30' -- time range 
    AND upper(during)::time BETWEEN time '8:30'AND time '18:00' 
    AND EXTRACT(MINUTE FROM lower(during)) IN (0, 30) -- only :00 or :30 
    AND EXTRACT(MINUTE FROM upper(during)) IN (0, 30) 
    AND lower_inc(during)   -- lower bound always incl. 
    AND upper_inc(during) = FALSE -- upper bound always excl. 
) 

在评论

其他问题如何限制秒和零点几秒,只有0?

最简单的方法:投射到timestamp(0)timestamptz(0)或使用此数据类型作为开始的列。我引述the manual here

timetimestampinterval接受一个可选的精度值p 指定的保留在 秒字段小数的位数。

+0

谢谢。优秀。它看起来像4.)也允许非半小时的时间,如8:15,8:0:01等。如何添加约束以便只允许半小时的时间? – Andrus

+0

@安德鲁斯:我补充说,还包括检查包容性/专属边界。您可以将项目拆分为单独的“CHECK”约束,并显示错误消息的名称...... –

+1

@ErwinBrandstetter第一个约束。这没有意义。也许他想要的是相同的日期,而不是日期/时间。 –