5

我有以下 2 个表:

Bookings
  - User ID

Shifts
  - Booking ID
  - Date

我想确保用户不会在同一日期预订超过一个班次。换句话说,字段 [Bookings.UserID, Shifts.date] 的唯一约束

我可以在 PostgreSQL 中执行此操作吗?

4

2 回答 2

7

PostgreSQL 中的索引在一张上,不能跨越多张表。

但是您可以通过适当的表格布局解决您的问题。您可能需要的是一个 n:m 关系,用 3 个表实现。然后有多种方法可以强制执行您的条件。

  • 您可以将日期冗余存储在预订中,创建主键(usr_id, shift_date)而不是使用多列外键(usr_id, shift_id)保证引用完整性:(shift_id, shift_date)

CREATE TABLE usr(
  usr_id serial PRIMARY KEY
 --, more?
);

CREATE TABLE shift(
  shift_id serial PRIMARY KEY
 ,shift_date date
 --, more?
);

这也需要先UNIQUE INDEX开启shift

CREATE UNIQUE INDEX shift_date_idx ON shift (shift_id, shift_date);

CREATE TABLE booking(
  usr_id int REFERENCES usr(usr_id)
 ,shift_id int
 ,shift_date date
 --, more?
 ,CONSTRAINT booking_pkey PRIMARY KEY (usr_id, shift_date)
 ,CONSTRAINT booking_fkey FOREIGN KEY (shift_id, shift_date)
 REFERENCES shift(shift_id, shift_date)
);
  • 另一种限制较少但也不太可靠的方法是在表格上使用触发器 来检查用户是否已经在新日期进行了轮班。可以在没有冗余日期列的情况下工作。ON INSERT OR UPDATEbooking

我会选择第一个解决方案。


PostgreSQL 中的视图也没有索引(据我所知,从 v9.1 开始)。

可以创建一个返回shift_date给定值的函数shift_id并声明它IMMUTABLE(尽管实际上并非如此)。有了它,您可以在表达式上创建多列索引:

CREATE function f_shift_date (shift_id int)
 RETURNS date LANGUAGE SQL IMMUTABLE AS
$BODY$
   SELECT shift_date FROM shift WHERE shift_id = $1;
$BODY$;

CREATE UNIQUE INDEX shift_date_idx ON booking (shift_id, f_shift_date(shift_id));

这将强制执行您的条件。但老实说,这很自杀。该索引依赖于您shift_id无限期地获得相同日期的条件。如果shift_date相对于它的 发生变化shift_id,则必须重新创建索引,否则将产生不正确的结果。所以,不要那样做

于 2012-08-13T16:39:10.840 回答
0

只是以上还不能解决你的问题?

ALTER TABLE shifts
ADD CONSTRAINT uk_shifts_book UNIQUE (booking_id, date);

如果您已经在班次上有引用列,那么您只需要在引用列和日期列上创建一个唯一约束(甚至是唯一索引)。

于 2012-08-13T17:50:01.293 回答