Ограничение PostgreSQL - только одна строка может иметь установленный флаг

У меня есть таблица PostgreSQL

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

Как установить ограничение, что только одна строка может иметь флаг actual, установленный на TRUE?

Ответ 1

Вы можете создать уникальный индекс в этом столбце только для истинных значений:

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

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

Ответ 2

Мой подход добавит еще одну особенность в единственное решение на основе индекса: автоматическая деактивация текущего флага при установке флага в другой строке.

Это предполагает, конечно, триггер.

Я также рекомендовал, как предложил Фрэнк Хейкенс, хранить "не фактическое" состояние как 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();