Postgresql: условно уникальное ограничение

117

Я хотел бы добавить ограничение, которое обеспечивает уникальность столбца только в части таблицы.

ALTER TABLE stop ADD CONSTRAINT myc UNIQUE (col_a) WHERE (col_b is null);

WHEREЧасть выше выдавать желаемое за действительное.

Как это сделать? Или мне следует вернуться к доске для рисования отношений?

EoghanM
источник
2
Обычно это делается. См. «Частичный уникальный индекс»
Крейг Рингер,
11
@yvesonline нет, это обычное уникальное ограничение. Плакат требует частичного уникального ограничения.
Craig Ringer

Ответы:

186

PostgreSQL не определяет частичное (т.е. условное) UNIQUEограничение, однако вы можете создать частичный уникальный индекс . PostgreSQL использует уникальные индексы для реализации уникальных ограничений, поэтому эффект тот же, вы просто не увидите ограничения, перечисленные в information_schema.

CREATE UNIQUE INDEX stop_myc ON stop (col_a) WHERE (col_b is NOT null);

См. Частичные индексы .

Крэйг Рингер
источник
24
Супер! Не интуитивно понятно, что «ограничение» не проявляется как ограничение, но, тем не менее, дает желаемую ошибкуERROR: duplicate key value violates unique constraint "stop_myc"
EoghanM
8
Стоит отметить, что это не позволит создавать FK, ссылающиеся на это частично уникальное поле.
ffflabs
12
Также стоит отметить, что эффекты этого индекса нельзя отложить. Если вам нужно выполнять массовые обновления, это может представлять проблему, поскольку уникальность проверяется после каждой строки, а не после оператора, как это было бы для ограничения, или после транзакции, как это было бы для отложенного ограничения.
sage88
37

уже было сказано, что PG не определяет частичное (т.е. условное) ограничение UNIQUE. Также в документации говорится, что предпочтительный способ добавления уникального ограничения в таблицу - ADD CONSTRAINT Уникальные индексы.

Предпочтительный способ добавить уникальное ограничение к таблице - ALTER TABLE ... ADD CONSTRAINT. Использование индексов для обеспечения уникальных ограничений можно рассматривать как деталь реализации, к которой не следует обращаться напрямую. Однако следует помнить, что нет необходимости вручную создавать индексы для уникальных столбцов; это просто дублирует автоматически созданный индекс.

Есть способ реализовать это с помощью ограничений исключения (спасибо @dukelion за это решение)

В вашем случае это будет выглядеть так

ALTER TABLE stop ADD CONSTRAINT myc EXCLUDE (col_a WITH =) WHERE (col_b IS null);
Петр Еременко
источник
при таком подходе вы не используете «использование» для определения метода индекса, так что это может быть очень медленным, или postgres создает для него индекс по умолчанию? Этот метод является каноническим, но не лучшим выбором! Я думаю, вам понадобится фраза "using" с индексом, чтобы сделать этот выбор лучшим.
Натан Медейрос
10
Хотя решение exclude и работает медленнее, но его преимущество в том, что оно откладывается (и по умолчанию откладывается до конца оператора). Напротив, принятое решение уникального индекса не может быть отложено (и проверяется после каждого изменения строки). Таким образом, массовое обновление часто невозможно, потому что шаги во время обновления нарушили бы уникальное ограничение, даже если оно не будет нарушено в конце оператора атомарного обновления.
sage88
1
Эта заметка была удалена из документации в августе 2015 г.
Раниз