【问题标题】:PostgreSQL constraint - only one row can have flag setPostgreSQL 约束 - 只有一行可以设置标志
【发布时间】:2015-01-27 09:21:34
【问题描述】:

我有一个 PostgreSQL 表

CREATE TABLE my_table
(
  id serial NOT NULL,
  name text,
  actual boolean DEFAULT false,
  CONSTRAINT my_table_pkey PRIMARY KEY (id),
);

如何设置只有一行可以将actual 标志设置为TRUE 的约束?

【问题讨论】:

  • 建议:false 占用一个字节,NULL 不花费任何成本。当所有记录都具有相同的值时,除了一个,NULL 可能是更好(更便宜)的解决方案。

标签: sql postgresql


【解决方案1】:

您只能在该列上为真值创建唯一索引:

create unique index on my_table (actual) 
where actual = true;

SQLFiddle:http://sqlfiddle.com/#!15/91f62/1

【讨论】:

  • 应该是actual is true
  • @FrozenFlame:不,actual = true 非常好。事实上,where actual 就足够了。
【解决方案2】:

我的方法会为仅基于索引的解决方案添加另一个功能:在另一行设置标志时自动停用当前标志。

这当然会涉及到一个触发器。

我还建议按照 Frank Heikens 的建议,将“非实际”状态存储为 null 而不是 false。在 postgresql 中,每个 null 值都与另一个 null 值不同,因此唯一性约束很容易解决:我们可以只允许一个 true 值,并根据需要允许多个 null 值。

这是我的实现:

CREATE TABLE my_table
(
  id serial NOT NULL,
  name text,
  actual boolean,
  CONSTRAINT my_table_pkey PRIMARY KEY (id),
  CONSTRAINT actual_not_false CHECK(actual != false)
);

.

CREATE UNIQUE INDEX ON my_table USING btree(actual nulls LAST);

.

CREATE OR REPLACE FUNCTION ensure_only_one_enabled_state_trigger()
 RETURNS trigger
AS $function$
BEGIN
    -- nothing to do if updating the row currently enabled
    IF (TG_OP = 'UPDATE' AND OLD.actual = true) THEN
        RETURN NEW;
    END IF;

    -- disable the currently enabled row
    EXECUTE format('UPDATE %I.%I SET actual = null WHERE actual = true;', TG_TABLE_SCHEMA, TG_TABLE_NAME);

    -- enable new row
    NEW.actual := true;
    RETURN NEW;
END;
$function$
LANGUAGE plpgsql;

.

CREATE TRIGGER my_table_only_one_enabled_state
BEFORE INSERT OR UPDATE OF actual ON my_table
FOR EACH ROW WHEN (NEW.actual = true)
EXECUTE PROCEDURE ensure_only_one_enabled_state_trigger();

【讨论】:

  • 很好的解决方案,谢谢!使用null 而不是false 是一种好习惯吗?现在,我正在考虑在我的所有表格中使用 CHECK (column != false)。再次感谢。
【解决方案3】:

这应该可以通过排除约束来实现。对于您的情况:

CREATE TABLE my_table
(
    id serial NOT NULL,
    name text,
    actual boolean DEFAULT false,
    CONSTRAINT my_table_pkey PRIMARY KEY (id),
    EXCLUDE (actual WITH =) WHERE (actual)
);

测试:

INSERT INTO my_table VALUES (1, 'something', false);
INSERT INTO my_table VALUES (2, 'something_else', true);

那么下面就是违反约束了

INSERT INTO my_table VALUES (3, 'third_thing', true);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-19
    • 2015-07-17
    • 1970-01-01
    • 1970-01-01
    • 2015-09-12
    • 2010-09-15
    • 2020-10-23
    相关资源
    最近更新 更多