创建 PostgreSQL 约束以防止唯一组合行

And*_*iuk 11 postgresql exclusion-constraint

想象一下你有一个简单的表:

name | is_active
----------------
A    | 0
A    | 0
B    | 0
C    | 1
...  | ...
Run Code Online (Sandbox Code Playgroud)

我需要创建一个特殊的唯一约束,该约束在以下情况下失败:相同is_active值不能共存不同的name值。

允许条件示例:

注意:简单的多列唯一索引不允许这样的组合。

A    | 0
A    | 0
B    | 0
Run Code Online (Sandbox Code Playgroud)

允许条件示例:

A    | 0
B    | 1
Run Code Online (Sandbox Code Playgroud)

失败条件示例:

A    | 0
A    | 1
-- should be prevented, because `A 0` exists
-- same name, but different `is_active`
Run Code Online (Sandbox Code Playgroud)

理想情况下,我需要唯一约束或唯一部分索引。触发器对我来说更成问题。

双重A,0允许,但(A,0) (A,1)不是。

ype*_*eᵀᴹ 19

您可以使用排除约束btree_gist

-- This is needed
CREATE EXTENSION btree_gist;
Run Code Online (Sandbox Code Playgroud)

然后我们添加一个约束,说:

“我们不能有 2 行具有相同name和不同的is_active

ALTER TABLE table_name
  ADD CONSTRAINT only_one_is_active_value_per_name
    EXCLUDE  USING gist
    ( name WITH =, 
      is_active WITH <>      -- if boolean, use instead:
                             -- (is_active::int) WITH <>
    );
Run Code Online (Sandbox Code Playgroud)

一些注意事项:

  • is_active可以是整数或布尔值,对排除约束没有区别。(实际上确实如此,如果列是布尔值,则需要使用(is_active::int) WITH <>.)
  • 行,其中nameis_active为空将被限制被忽略,从而允许。
  • 只有当表有更多列时,约束才有意义。否则,如果表只有这 2 列,则单独UNIQUE约束(name)会更容易也更合适。我没有看到存储多个相同行的任何理由。
  • 该设计违反了 2NF。虽然排除约束将使我们免于更新异常,但它可能不会免于性能问题。例如,如果您有 1000 行name = 'A'并且想要将 is_active 状态从 0 更新为 3,则必须更新所有 1000 行。您应该检查规范化设计是否会更有效。(在这种情况下,规范化含义是从表中删除 is_active 状态并添加名称为 is_active 的 2 列表和对 的唯一约束(name)。如果is_active是布尔值,则可以完全剥离它,而额外的表只是一个单列表,存储只有“活动”名称。)