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,并且只能以 30 分钟的间隔进行预订。如何在值中添加约束,以便它只允许在工作时间进行预订:

  1. tsrange 中的开始日期和结束日期始终相同。
  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

将这些或其中一些约束添加到此表是否合理?如果是,如何添加?如果这有帮助,可以更改计划表结构。

4

2 回答 2

4

您可以解决除第 3 项之外的所有问题。使用简单的CHECK约束

使用专用范围功能 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 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)使用此数据类型作为列的开头。我在这里引用手册

time, timestamp, 并interval接受一个可选的精度值p ,它指定秒字段中保留的小数位数。

于 2012-11-25T22:24:46.573 回答
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
于 2012-11-25T23:03:54.550 回答