PostgreSQL 约束 - 只有一行可以设置标志
PostgreSQL constraint - only one row can have flag set
我有一个 PostgreSQL table
CREATE TABLE my_table
(
id serial NOT NULL,
name text,
actual boolean DEFAULT false,
CONSTRAINT my_table_pkey PRIMARY KEY (id),
);
如何设置一个约束条件,即只有一行可以将 actual
标志设置为 TRUE
?
您可以仅为真值在该列上创建唯一索引:
create unique index on my_table (actual)
where actual = true;
SQLFiddle: http://sqlfiddle.com/#!15/91f62/1
我的方法会向仅基于索引的解决方案添加另一个功能:在另一行上设置标志时自动停用当前标志。
这当然涉及到触发器。
我还建议按照 Frank Heikens 的建议,将 "not actual" 状态存储为 null
而不是 false
。在 postgresql 中,每个 null
值都不同于另一个 null
值,因此唯一性约束很容易解决:我们可以只允许一个 true
值,而允许多个 [=14] =] 必要的值。
这是我的实现:
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();
这应该可以通过排除约束来实现。对于您的情况:
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);
我有一个 PostgreSQL table
CREATE TABLE my_table
(
id serial NOT NULL,
name text,
actual boolean DEFAULT false,
CONSTRAINT my_table_pkey PRIMARY KEY (id),
);
如何设置一个约束条件,即只有一行可以将 actual
标志设置为 TRUE
?
您可以仅为真值在该列上创建唯一索引:
create unique index on my_table (actual)
where actual = true;
SQLFiddle: http://sqlfiddle.com/#!15/91f62/1
我的方法会向仅基于索引的解决方案添加另一个功能:在另一行上设置标志时自动停用当前标志。
这当然涉及到触发器。
我还建议按照 Frank Heikens 的建议,将 "not actual" 状态存储为 null
而不是 false
。在 postgresql 中,每个 null
值都不同于另一个 null
值,因此唯一性约束很容易解决:我们可以只允许一个 true
值,而允许多个 [=14] =] 必要的值。
这是我的实现:
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();
这应该可以通过排除约束来实现。对于您的情况:
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);