Postgresql: vincolo univoco condizionale


116

Vorrei aggiungere un vincolo che impone l'unicità su una colonna solo in una parte di una tabella.

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

La WHEREparte sopra è un pio desiderio.

Qualche modo per farlo? O devo tornare al tavolo da disegno relazionale?


2
Comunemente fatto. Vedi "indice univoco parziale"
Craig Ringer

11
@yvesonline no, questo è un vincolo univoco regolare. Il poster vuole un vincolo univoco parziale .
Craig Ringer

Risposte:


186

PostgreSQL non definisce un UNIQUEvincolo parziale (cioè condizionale) - tuttavia, puoi creare un indice univoco parziale . PostgreSQL utilizza indici univoci per implementare vincoli univoci, quindi l'effetto è lo stesso, semplicemente non vedrai il vincolo elencato in information_schema.

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

Vedi indici parziali .


24
Super! Non intuitivo che il "vincolo" non si presenti come un vincolo, ma comunque dà l'errore desiderato diERROR: duplicate key value violates unique constraint "stop_myc"
EoghanM

7
Vale la pena notare che questo non consentirà di creare riferimenti a FK in quel campo parzialmente unico.
ffflabs

11
Vale anche la pena notare che gli effetti di questo indice non possono essere differiti. Se è necessario eseguire aggiornamenti in blocco, ciò potrebbe presentare un problema poiché l'unicità viene verificata dopo ogni riga, non dopo l'istruzione come sarebbe per un vincolo o dopo la transazione come sarebbe per un vincolo differibile.
sage88

37

è già stato detto che PG non definisce un vincolo UNICO parziale (cioè condizionale). Inoltre, la documentazione dice che il modo preferito per aggiungere un vincolo univoco a una tabella è gli ADD CONSTRAINT indici univoci

Il modo preferito per aggiungere un vincolo univoco a una tabella è ALTER TABLE ... ADD CONSTRAINT. L'utilizzo di indici per applicare vincoli univoci potrebbe essere considerato un dettaglio di implementazione a cui non si dovrebbe accedere direttamente. Si dovrebbe, tuttavia, essere consapevoli del fatto che non è necessario creare manualmente indici su colonne univoche; così facendo si duplicherebbe semplicemente l'indice creato automaticamente.

C'è un modo per implementarlo utilizzando i vincoli di esclusione , (grazie a @dukelion per questa soluzione)

Nel tuo caso sembrerà

ALTER TABLE stop ADD CONSTRAINT myc EXCLUDE (col_a WITH =) WHERE (col_b IS null);

con quell'approccio non usi "using" per definire il metodo dell'indice, quindi può essere estremamente lento o postgres crea un indice predefinito su quello? Quel metodo è la scelta canonica, ma non mai la scelta migliore! Penso che avrai bisogno di una clausola "using" con indice per fare in modo che la scelta sia la migliore.
Natan Medeiros

10
Sebbene più lenta, il vantaggio della soluzione di esclusione è che è rinviabile (e per impostazione predefinita rimanda fino alla fine dell'istruzione). Al contrario, la soluzione di indice univoco accettata non può essere differita (e viene controllata dopo ogni modifica di riga). Quindi un aggiornamento in blocco spesso non è possibile perché i passaggi durante l'aggiornamento violerebbero il vincolo univoco, anche se non verrebbe violato alla fine dell'istruzione di aggiornamento atomico.
sage88

1
Questa nota è stata rimossa dai documenti
nell'agosto
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.