Ответ 1
Вы можете создать уникальный индекс в этом столбце только для истинных значений:
create unique index on my_table (actual)
where actual = true;
SQLFiddle: http://sqlfiddle.com/#!15/91f62/1
У меня есть таблица PostgreSQL
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
Мой подход добавит еще одну особенность в единственное решение на основе индекса: автоматическая деактивация текущего флага при установке флага в другой строке.
Это предполагает, конечно, триггер.
Я также рекомендовал, как предложил Фрэнк Хейкенс, хранить "не фактическое" состояние как 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();