Postgresql: restrição única condicionalmente

116

Eu gostaria de adicionar uma restrição que impõe exclusividade em uma coluna apenas em uma parte de uma tabela.

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

A WHEREparte acima é um pensamento positivo.

Alguma maneira de fazer isso? Ou devo voltar para a prancheta relacional?

EoghanM
fonte
2
Normalmente feito. Ver "índice exclusivo parcial"
Craig Ringer
11
@yvesonline não, essa é uma restrição única regular. O pôster deseja uma restrição única parcial .
Craig Ringer

Respostas:

186

O PostgreSQL não define uma UNIQUErestrição parcial (ou seja, condicional) - no entanto, você pode criar um índice parcial exclusivo . PostgreSQL usa índices únicos para implementar restrições únicas, então o efeito é o mesmo, você apenas não verá a restrição listada em information_schema.

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

Veja os índices parciais .

Craig Ringer
fonte
24
Super! Não é intuitivo que a "restrição" não apareça como uma restrição, mas ainda assim forneça o erro desejado deERROR: duplicate key value violates unique constraint "stop_myc"
EoghanM
7
Vale ressaltar que isto não permitirá a criação de FKs referencig aquele campo parcialmente único.
ffflabs
11
Também é importante notar que os efeitos desse índice não podem ser adiados. Se você precisar realizar atualizações em massa, isso pode representar um problema, pois a exclusividade é verificada após cada linha, não após a instrução como seria para uma restrição ou após a transação como seria para uma restrição adiável.
sage88,
37

já foi dito que o PG não define uma restrição UNIQUE parcial (isto é, condicional). Além disso, a documentação diz que a forma preferida de adicionar uma restrição única a uma tabela é ADD CONSTRAINT Índices Únicos

A maneira preferida de adicionar uma restrição exclusiva a uma tabela é ALTER TABLE ... ADD CONSTRAINT. O uso de índices para impor restrições exclusivas pode ser considerado um detalhe de implementação que não deve ser acessado diretamente. Deve-se, entretanto, estar ciente de que não há necessidade de criar índices manualmente em colunas exclusivas; fazer isso apenas duplicaria o índice criado automaticamente.

Existe uma maneira de implementá-lo usando restrições de exclusão , (agradeça a @dukelion por esta solução)

No seu caso, parecerá

ALTER TABLE stop ADD CONSTRAINT myc EXCLUDE (col_a WITH =) WHERE (col_b IS null);
Peter Yeremenko
fonte
nessa abordagem você não usa "usando" para definir o método de índice, então isso pode ser extremamente lento ou o postgres cria um índice padrão nisso? Esse método é a escolha canônica, mas nem sempre a melhor escolha! Acho que você vai precisar de uma cláusula "using" com index para fazer essa escolha ser a melhor.
Natan Medeiros
10
Embora mais lenta, a vantagem da solução de exclusão é que ela pode ser adiada (e, por padrão, adia até o final da instrução). Em contraste, a solução de índice exclusivo aceita não pode ser adiada (e é verificada após cada alteração de linha). Portanto, uma atualização em massa geralmente não é possível porque as etapas durante a atualização violariam a restrição exclusiva, mesmo que não fosse violada no final da instrução de atualização atômica.
sage88,
1
Esta nota foi removida dos documentos em agosto de 2015
Raniz