2012-11-06 12 views
7

Abbiamo una tabella con un vincolo univoco su di esso, per feedback lasciato da un utente, per un altro, in relazione a una vendita.Vincolo univoco con righe cancellate escluse

ALTER TABLE feedback 
ADD CONSTRAINT unique_user_subject_and_sale 
UNIQUE (user_id, subject_id, sale_id) 

Ciò garantisce che non si verifichino accidentalmente righe di feedback duplicate.

Attualmente a volte cancelliamo il feedback lasciato in errore e l'utente ha lasciato di nuovo. Vogliamo cambiare a soft-delete:

ALTER TABLE feedback 
ADD COLUMN deleted_at timestamptz 

Se deleted_at IS NOT NULL, prendere in considerazione le valutazioni cancellato, anche se abbiamo ancora la pista di controllo nel nostro DB (e probabilmente mostrarlo fantasma fuori per gli amministratori del sito).

Come possiamo mantenere il nostro vincolo univoco quando stiamo usando soft-delete come questo? È possibile senza utilizzare un vincolo più generale CHECK() che esegue un controllo di aggregazione (non ho mai provato a utilizzare il controllo di vincolo come questo).

È come se fosse necessario aggiungere una clausola WHERE al vincolo.

risposta

14

tuo indice univoco, in seguito modificati fuori .

CREATE UNIQUE INDEX feedback_unique_user_subject_and_sale_null 
ON feedback(user_id, subject_id, sale_id) 
WHERE deleted_at IS NULL 

Il tuo indice univoco ha almeno due effetti collaterali che potrebbero causare qualche problema.

  1. In altre tabelle, non è possibile impostare un vincolo di chiave esterna che fa riferimento a "feedback". Un riferimento a chiave esterna richiede una combinazione di colonne da dichiarare come primary key o unique.
  2. L'indice univoco consente più righe che differiscono da solo nel timestamp "deleted_at". Quindi è possibile finire con le righe che assomigliano all'esempio qui sotto. Se questo è un problema dipende dall'applicazione.

Esempio

user_id subject_id sale_id deleted_at 
-- 
1  1   1  2012-01-01 08:00:01.33 
1  1   1  2012-01-01 08:00:01.34 
1  1   1  2012-01-01 08:00:01.35 

documenti di PostgreSQL questo tipo di indice come un indice parziale , dovrebbe avete bisogno a Google qualche volta. Altre piattaforme usano termini differenti per questo - indice filtrato è uno. È possibile limitare i problemi in una certa misura con una coppia di indici parziali.

CREATE UNIQUE INDEX feedback_unique_user_subject_and_sale_null 
ON feedback(user_id, subject_id, sale_id) 
WHERE deleted_at IS NULL 

CREATE UNIQUE INDEX feedback_unique_user_subject_and_sale_not_null 
ON feedback(user_id, subject_id, sale_id) 
WHERE deleted_at IS NOT NULL 

Ma vedo alcun motivo per andare a questo disturbo, soprattutto in considerazione i potenziali problemi con chiavi esterne. Se la tabella assomiglia a questo

create table feedback (
    feedback_id integer primary key, 
    user_id ... 
    subject_id ... 
    sale_id ... 
    deleted_at ... 
    constraint unique_user_subj_sale 
    unique (user_id, subject_id, sale_id) 
); 

allora tutto ciò che serve è che vincolo univoco su {user_id, icLsoggetto, sale_id}. Potresti inoltre considerare di fare tutte le eliminazioni usando la colonna "deleted_at" invece di fare una cancellazione hard.

+0

Non ho inserito lo schema completo, ma il feedback ha una chiave primaria (seriale) intera. Inoltre, user_id, sale_id e subject_id sono dichiarati come chiavi esterne (e hanno indici). Abbiamo solo bisogno di rafforzare l'unicità come un ulteriore vincolo. – d11wtq

+0

E sì, va bene per l'indice univoco per consentire le righe che differiscono solo nel timestamp deleted_at ... questa è la cosa che stavo cercando di raggiungere;) – d11wtq

+1

Se si dispone di una chiave primaria seriale eb) una parziale indice come hai descritto e come ho provato, hai ancora un potenziale problema con altre tabelle che fanno riferimento al "feedback". Supponiamo che le tre righe che ho postato nell'esempio precedente abbiano le chiavi primarie seriali 1, 2 e 3. Quale di queste fa scegliere una tabella di riferimento per la sua chiave esterna? In che modo tutte le tabelle di riferimento sapranno selezionare lo * stesso * valore? –

6

Nonostante il fatto che la documentazione di PostgreSQL consiglia di non utilizzare un indice univoco al posto di un vincolo (se il punto è quello di avere un vincolo), sembra che si può fare

CREATE UNIQUE INDEX feedback_unique_user_subject_and_sale 
ON feedback(user_id, subject_id, sale_id) 
WHERE deleted_at IS NULL 
+0

Indicato come "indice parziale". Vedi @ Post di Catcall per limitazioni importanti. –