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:00 至 18:00,且只能以 30 分钟为间隔进行预订。 如何向 while 值添加约束,使其仅允许在工作时间进行预订:
向该表添加这些或其中一些约束是否合理? 如果有,如何添加? 如果有帮助的话,可以更改计划表结构。
CHECK
约束来解决除第 3 项之外的所有问题:
使用专用的 范围函数
lower(anysrange)
和 upper(anyrange)
访问范围的下限/上限。
1.) tsrange 中的开始日期和结束日期始终相同。
CONSTRAINT schedule_same_day
CHECK (lower(during)::date = upper(during)::date)
2.) 开始和结束日期不能是周六和周日
isodow
,而不是 dow
来表达更简单。
CONSTRAINT schedule_no weekend
CHECK (EXTRACT(ISODOW FROM lower(during)) < 6) -- upper is on same day
3.) 开始和结束日期不能出现在公共假期表中
为此,您需要一个触发器,例如:
CREATE OR REPLACE FUNCTION trg_during_no_holy()
RETURNS trigger
LANGUAGE plpgsql AS
$func$
BEGIN
IF EXISTS (SELECT FROM holiday WHERE day = lower(NEW.during)) THEN
RAISE EXCEPTION 'Day too holy: %', lower(NEW.during);
END IF;
RETURN NEW;
END
$func$;
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.
)
更新:在 Postgres 14 或更高版本中使用
date_bin()
:
CONSTRAINT schedule_8_inc_to_18_exc_half_hours CHECK (
lower(during) < upper(during)
AND lower(during)::time >= time '08:00'
AND upper(during)::time <= time '18:00'
AND during = date_bin('30 min', during, '2000-01-01') -- only :00 or :30 exactly
AND lower_inc(during) -- lower bound always incl.
AND upper_inc(during) = false -- upper bound always excl.
)
这也强制执行您的附加要求“限制秒和小数秒”。
参见:
您需要更改表定义并添加一些
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 &&)
);
支票按此顺序
我们需要
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