2013-04-20 15 views
6

Voglio scrivere una singola istruzione Postgres SQL che dice cercare un utente con colore X e luminosità Y. Se quell'utente esiste, restituisce tutti i suoi dati di riga. In caso contrario, creare una nuova riga e passare ulteriori informazioni. I due prospetti separati avrebbero fare qualcosa di simile:Scrivi un Postgres Ottieni o crea query SQL

Select (color, brightness, size, age) FROM mytable WHERE color = 'X' AND brightness= 'Y'; 

Se questo non restituisce nulla, quindi eseguire questo:

INSERT INTO mytable (color, brightness, size, age) VALUES (X, Y, big, old); 

C'è un modo per combinare questi in una singola query ??

+0

Vedendo la clausola 'RETURNING' di postgresql nelle istruzioni' INSERT', inizialmente speravo che si potesse combinare un insert con una select in una istruzione usando un union, ma l'ho provato e sfortunatamente le letture e le scritture sono davvero impossibili da mixare quel modo. – didierc

risposta

15

In un DBMS SQL, l'approccio select-test-inserto è un errore: nulla impedisce un altro processo di inserire la riga "mancante" tra le select e insert istruzioni. Fatelo invece:

insert into mytable (color, brightness, size, age) 
select (color, brightness, size, age) from mytable 
where not exists (
    select 1 from 
    from mytable 
    where color = 'X' and brightness = 'Y' 
); 
SELECT (color, brightness, size, age) 
FROM mytable 
WHERE color = 'X' AND brightness= 'Y'; 

Dovresti essere in grado di passare l'intero testo come una singola "query" al DBMS. Potresti prendere in considerazione l'idea di trasformarlo in una stored procedure.

+0

Correlato a questa risposta e utile per una migliore comprensione: http://stackoverflow.com/a/13342031/399726 – BenjaminGolder

+0

Impossibile capire come questa risposta abbia raccolto così tanti voti contenenti un errore di sintassi. Non esiste alcuna opzione 'inserisci ... dove ...' per INSERT in Postgres. Probabilmente intendevi 'inserire in mytable (colore, luminosità, dimensione, età) SELECT 'X', 'Y', 1.2, 3.4 dove non esiste ...'. – greatvovan

4
with sel as (
    select color, brightness, size, age 
    from mytable 
    where color = 'X' and brightness = 'Y' 
), ins as (
    insert into mytable (color, brightness, size, age) 
    select 'X', 'Y', 6.2, 40 
    where not exists (
     select 1 from sel 
    ) 
    returning color, brightness, size, age 
) 
select color, brightness, size, age 
from ins 
union 
select color, brightness, size, age 
from sel 
2

Se le colonne partecipare a vincolo indice univoco è possibile utilizzare un approccio che è avaible a partire dalla versione 9.5:

INSERT INTO mytable (color, brightness, size, age) 
VALUES ('X', 'Y', 'big', 'old') 
ON CONFLICT (color) DO NOTHING; 

(ammesso che abbiate indice univoco color).

Docs sono Gere: https://www.postgresql.org/docs/9.5/static/sql-insert.html

+0

Questo non risponde alla domanda, che chiede come ottenere * o * creare * una riga. – Druska

1

Aggiunta la mia soluzione qui. È un po 'diverso dalle soluzioni @Clodoaldo Neto e @ astef.

WITH ins AS (
    INSERT INTO mytable (color, brightness, size, age) 
    VALUES ('X', 'Y', 'big', 'old') 
    ON CONFLICT (color) DO NOTHING 
    RETURNING * 
) 
SELECT * FROM ins 
UNION 
SELECT * FROM mytable 
    WHERE color = 'X'; 

ho trovato la soluzione di astef inadeguata per i miei scopi: non esegue la parte "get" di "ottenere o creare"! Se il valore esistesse già, non accadrebbe nulla.

L'unione alla fine dell'istruzione garantisce che se il valore non è stato inserito (poiché esisteva già), recuperiamo ancora tale valore dalla tabella.

+0

Ho trovato un problema con quanto sopra: Se c'è un conflitto, il contatore ID seriale della tabella aumenterà ad ogni esecuzione della query. Questo non è l'ideale –