2016-04-27 11 views
16

Vorrei utilizzare l'insert .. su confict do update .. sintassi con una tabella che ha vincoli univoci su due colonne. È possibile?Gestione dei conflitti Postgres con più vincoli unici

ad es. mytable ha vincoli univoci separati su col1 e col2.

posso scrivere:

INSERT INTO mytable(col1, col2, col3) values ('A', 'B', 0) ON CONFLICT DO NOTHING; 

Tuttavia questo non funziona:

INSERT INTO mytable(col1, col2, col3) VALUES ('A', 'B', 0) 
ON CONFLICT 
DO UPDATE SET col3 = EXCLUDED.col3 + 1; 

ERRORE: ON CONFLICT DO aggiornamento richiede specifica deduzione o il nome del vincolo

Questo non lo fa anche lavoro:

INSERT INTO mytable(col1, col2, col3) VALUES ('A', 'B', 0) 
ON CONFLICT (col1, col2) 
DO UPDATE SET col3 = EXCLUDED.col3 + 1; 

ERRORE: non esiste un vincolo univoco o di esclusione corrispondente alla specifica ON CONFLICT

Questa sintassi sembra essere progettata per un singolo vincolo univoco composto su due colonne, anziché due vincoli.

Esiste un modo per eseguire un aggiornamento condizionale se viene violato un vincolo univoco? Questa domanda How to upsert in Postgres on conflict on one of 2 columns? allude ad esso ma non fornisce la sintassi.

+0

IMHO la domanda non ha senso. Nel "caso semplice in conflitto": la chiave viene mantenuta e (alcuni) i campi dipendenti vengono aggiornati. Nel tuo caso, hai intenzione di aggiornare un'altra chiave (candidata). Infatti, si tenta di aggiornare entrambe le chiavi (candidate), che è oltre la mia logica. – wildplasser

+0

Ho aggiornato l'esempio per essere più realistico.L'idea è di mantenere aggiornata una colonna contatore che corrisponda a una colonna univoca o inserire zero se nessuno dei due esiste. –

+2

Aggiungere un vincolo extra 'UNIQUE (col1, col2)' probabilmente farà ciò che vuoi. (è logicamente ridondante, ma il modello di dati ha comunque poco o nessun senso) – wildplasser

risposta

14

La clausola ON CONFLICT richiede un unico vincolo univoco quando viene richiesto a DO UPDATE. Quando viene definita una chiave primaria, è sufficiente fare semplicemente riferimento al nome della colonna; che è l'esempio dominante che si tende a trovare.

si parla che avete 'vincoli univoci separati su col1 e col2', quindi mi potrebbe supporre la tua definizione della tabella è simile a questo:

CREATE TABLE mytable(  
    col1 varchar UNIQUE,  
    col2 varchar UNIQUE,  
    col3 int 
); 

Ma la query fa riferimento a un vincolo composito; piuttosto che vincoli separati. Una definizione modificato tabella come questa:

CREATE TABLE mytable2( 
    col1 varchar UNIQUE, 
    col2 varchar UNIQUE, 
    col3 int, 
    CONSTRAINT ux_col1_col2 UNIQUE (col1,col2) 
); 

avrebbe funzionato con la query di cui sopra:

INSERT INTO mytable(col1, col2, col3) VALUES ('A', 'B', 0) 
ON CONFLICT (col1, col2) 
DO UPDATE SET col3 = EXCLUDED.col3 + 1; 

Si può fare riferimento a questo vincolo unico sia come ON CONFLICT (col1, col2) o come ON CONFLICT ON CONSTRAINT ux_col1_col2.

Ma aspettate, c'è di più ...

The idea is to keep a counter column up to date which matches on either unique column, or insert zero if neither exists...

Questo è un percorso diverso da quello che stai prendendo qui. Le corrispondenze "su una colonna univoca" consentono la corrispondenza su entrambi, o nessuno dei due. Se capisco il tuo intento, hai solo una singola etichetta e incrementa i contatori sui record applicabili. Quindi:

CREATE TABLE mytable2( 
    col1 varchar PRIMARY KEY, 
    col3 int 
); 
INSERT INTO mytable2(col1,col3) 
SELECT incr_label,0 
FROM (VALUES ('A'),('B'),('C')) as increment_list(incr_label) 
ON CONFLICT (col1) 
DO UPDATE SET col3 = mytable2.col3 + 1 
RETURNING col1,col3;