2013-04-20 5 views
6

Ich möchte eine einzelne Postgres-SQL-Anweisung schreiben, die nach einem Benutzer mit Farbe X und Helligkeit Y suchen. Wenn dieser Benutzer vorhanden ist, alle Zeilendaten zurückgeben. Wenn nicht, erstellen Sie eine neue Zeile und übergeben Sie zusätzliche Informationen. Die beiden getrennten Aussagen würde so etwas tun:Schreiben Sie eine Postgres Get oder Create SQL-Abfrage

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

Wenn das nicht etwas zurück, dann folgt ausführen:

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

Gibt es eine Möglichkeit, diese in einer einzigen Abfrage zu kombinieren ??

+0

Mit der 'RETURNING'-Klausel-Erweiterung von postgresql auf' INSERT'-Anweisungen hatte ich zunächst gehofft, man könne eine Einfügung mit einer Auswahl in einer Anweisung mit einer Union kombinieren, aber ich habe es versucht und leider sind Lesen und Schreiben wirklich unmöglich zu mischen dieser Weg. – didierc

Antwort

15

In einer SQL-DBMS, die select-Test-Insert-Ansatz ist ein Fehler: Nichts hindert einen weiteren Prozess die "fehlenden" Reihe zwischen select und insert Erklärungen einfügen. Tun Sie dies statt:

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'; 

Sie sollten fähig sein, dass die gesamten Text als eine einzige „Abfrage“ zu übergeben an das DBMS. Sie sollten in Betracht ziehen, es zu einer gespeicherten Prozedur zu machen.

+0

Im Zusammenhang mit dieser Antwort und nützlich für ein besseres Verständnis: http://Stackoverflow.com/a/13342031/399726 – BenjaminGolder

+0

Kann nicht verstehen, wie diese Antwort so viele Stimmen gesammelt hat, die einen Syntaxfehler enthalten. Es gibt keine Option "Einfügen ... wo ..." für INSERT in Postgres. Du meintest wahrscheinlich 'einfügen in mytable (Farbe, Helligkeit, Größe, Alter) SELECT 'X', 'Y', 1.2, 3.4 wo nicht existiert ...'. – 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

Wenn Ihre Spalten in eindeutiger Index Einschränkung teilnehmen können Sie einen Ansatz verwenden, das seit Version 9.5 verfügbar ist:

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

(vorausgesetzt, Sie auf color eindeutigen Index).

Docs sind gere: https://www.postgresql.org/docs/9.5/static/sql-insert.html

+0

Dies beantwortet nicht die Frage, die fragt, wie man eine Zeile * erhält * oder * erstellt *. – Druska

1

hier meine Lösung Hinzufügen. Es ist ein bisschen anders als @Clodoaldo Neto und @ astef's Lösungen.

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'; 

Ich fand astef Lösung für meine Zwecke unzureichend: es nicht ausführen den Teil von „erhalten oder schaffen“ „get“! Wenn der Wert bereits existiert, würde nichts passieren.

Die Union am Ende der Anweisung stellt sicher, dass der Wert aus der Tabelle abgerufen wird, wenn der Wert nicht eingefügt wurde (da er bereits vorhanden war).

+0

Ich habe ein Problem mit dem oben genannten gefunden: Wenn es einen Konflikt gibt, wird der serielle ID-Zähler Ihrer Tabelle bei jeder Ausführung der Abfrage erhöht. Das ist nicht ideal. –

Verwandte Themen