如何使用Postgres EXCLUDE约束来防止插入两个主要行?

时间:2013-06-20 14:03:02

标签: sql postgresql database-schema

Postgres 9.0支持排除约束,它类似于一般的唯一约束(http://www.postgresql.org/docs/9.0/static/sql-createtable.html#SQL-CREATETABLE-EXCLUDE)。

我认为这可能是解决此问题的好方法,但我无法弄清楚如何正确使用排除约束。

这是问题所在: 我有一张这样的桌子:

 CREATE TABLE emails (
     id integer NOT NULL,
     email character varying(255),
     "primary" boolean DEFAULT false,
     user_id integer,
 );

我想确保每个唯一user_id只有一行"primary"等于true。我尝试使用这样的排除约束:

 ALTER TABLE emails ADD CONSTRAINT one_primary_email_per_user EXCLUDE USING gist (user_id WITH =, "primary" WITH &);

Postgres拒绝了:

 ERROR:  data type boolean has no default operator class for access method "gist"
 HINT:  You must specify an operator class for the index or define a default operator class for the data type.

我再次尝试将布尔列转换为bit

 ALTER TABLE emails ADD CONSTRAINT one_primary_email_per_user EXCLUDE  (user_id WITH =, (case "primary" when 't' then '1'::bit else '0'::bit end) WITH &);

那不起作用。似乎&(bit,bit)不是运算符类bit_ops的一部分:

 ERROR:  operator &(bit,bit) is not a member of operator family "bit_ops"
 DETAIL:  The exclusion operator must be related to the index operator class for the constraint.

查看http://www.leadum.com/downloads/dbscribe/samples/postgresql/web_modern/opclass/main/790197862.html,似乎bit_ops只包含排序比较运算符(>,<,=,> =,< =)而不包括按位运算符。我不确定为什么会这样。

这种排除是否可以通过EXCLUDE约束进行?有更好的方法吗?

感谢您的帮助!

1 个答案:

答案 0 :(得分:12)

为此创建部分唯一索引应该更简单:

create unique index on emails(email) where (primary);

(与此答案正交,但由于您询问错误消息:对于排除约束,您需要添加btree_ginbtree_gist扩展名以供btree运算符使用。 )