多字段UNIQUE约束是否可以包含多个表中的字段?

时间:2012-08-13 14:28:53

标签: postgresql database-design join constraints unique-constraint

我有以下2个表格:

Bookings
  - User ID

Shifts
  - Booking ID
  - Date

我想确保用户未在同一天内多次预订。换句话说,字段[Bookings.UserID,Shifts.date]

的UNIQUE约束

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

2 个答案:

答案 0 :(得分:7)

PostgreSQL中的索引位于 one 上,不能跨越多个表。

但您可以使用正确的表格布局来解决您的问题。您可能需要的是一个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 UPDATE上使用触发器 booking检查用户是否已使用新日期进行转换。可以在没有冗余日期列的情况下工作。

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


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发生了变化,则必须重新创建索引,否则会产生错误的结果。所以,不要那样做

答案 1 :(得分:0)

上述问题无法解决您的问题?

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

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