Das Einfügen einer einzelnen Zeile in eine Tabelle ist das, was Ihnen in den Sinn kommt, wenn Sie an die INSERT-Anweisung in PostgreSQL denken. Es hat jedoch noch ein paar Tricks mehr auf Lager! Lesen Sie weiter, um einige der interessanteren Dinge zu entdecken, die Sie mit INSERT machen können.
Massenkopieren
Angenommen, Sie möchten regelmäßig Snapshots einer Tabelle erfassen – alle Zeilen in der Tabelle sollten in eine andere Tabelle kopiert werden, wobei eine zusätzliche Zeitstempelspalte angibt, wann der Snapshot erstellt wurde. So können Sie die Tabelle beim ersten Mal erstellen und füllen:
demo=# SELECT * FROM mytable;
ticker | quote
--------+-------
FOO | $4.01
BAR | $1.42
(2 rows)
demo=# CREATE TABLE snaps_of_mytable AS
demo-# SELECT current_timestamp AS snapped_at, *
demo-# FROM mytable;
SELECT 2
demo=#
demo=# SELECT * FROM snaps_of_mytable ;
snapped_at | ticker | quote
-----------------------------+--------+-------
2018-10-09 04:16:22.3613+00 | FOO | $4.01
2018-10-09 04:16:22.3613+00 | BAR | $1.42
(2 rows)
Und von da an können Sie den INSERT..SELECT
verwenden Form der INSERT-Anweisung, um Zeilen aus einer Tabelle zu kopieren und in eine andere einzufügen. Sie können auch zusätzliche Werte in die Zeile der Zieltabelle eingeben.
demo=# INSERT INTO snaps_of_mytable
demo-# SELECT current_timestamp AS snapped_at, *
demo-# FROM mytable;
INSERT 0 2
demo=#
demo=# SELECT * FROM snaps_of_mytable ;
snapped_at | ticker | quote
-------------------------------+--------+-------
2018-10-09 04:16:22.3613+00 | FOO | $4.01
2018-10-09 04:16:22.3613+00 | BAR | $1.42
2018-10-09 04:18:53.432224+00 | BAR | $1.42
2018-10-09 04:18:53.432224+00 | FOO | $4.10
(4 rows)
Upserts
In PostgreSQL 9.5 ist der ON CONFLICT
-Klausel wurde zu INSERT hinzugefügt. Dadurch können Anwendungsentwickler weniger Code schreiben und mehr Arbeit in SQL erledigen.
Hier ist eine Tabelle mit Schlüssel-Wert-Paaren:
demo=# SELECT * FROM kv;
key | value
------+-----------
host | 127.0.0.1
port | 5432
(2 rows)
Ein häufiger Anwendungsfall besteht darin, eine Zeile nur dann einzufügen, wenn sie nicht vorhanden ist – und falls doch, nicht zu überschreiben. Dies geschieht mit dem ON CONFLICT..DO NOTHING
Klausel der INSERT-Anweisung:
demo=# INSERT INTO kv (key, value) VALUES ('port', '3306')
demo-# ON CONFLICT (key) DO NOTHING;
INSERT 0 0
demo=# SELECT * FROM kv;
key | value
------+-----------
host | 127.0.0.1
port | 5432
(2 rows)
Eine andere häufige Verwendung besteht darin, eine Zeile einzufügen, wenn sie nicht vorhanden ist, und den Wert zu aktualisieren, falls dies der Fall ist. Dies kann mit dem ON CONFLICT..DO UPDATE
erfolgen Klausel.
demo=# INSERT INTO kv (key, value) VALUES ('host', '10.0.10.1')
demo-# ON CONFLICT (key) DO UPDATE SET value=EXCLUDED.value;
INSERT 0 1
demo=# INSERT INTO kv (key, value) VALUES ('ssl', 'off')
demo-# ON CONFLICT (key) DO UPDATE SET value=EXCLUDED.value;
INSERT 0 1
demo=# SELECT * FROM kv;
key | value
------+-----------
host | 10.0.10.1
port | 5432
ssl | off
(3 rows)
Im ersten Fall oben wurde der Wert von „host“ mit dem neuen Wert überschrieben, und im zweiten Fall wurde der Wert von „ssl“ als dritte Zeile eingefügt.
Noch ausgefeiltere Anwendungsfälle lassen sich mit DO UPDATE
realisieren . Betrachten Sie die folgende Tabelle, in der es zusätzlich zu Schlüssel und Wert eine Spalte namens „Akkumulieren“ gibt. Für Zeilen, in denen akkumulieren wahr ist, sollen die Werte als kommagetrennte Zeichenfolge akkumuliert werden. Für andere Zeilen sind die Werte einwertig.
demo=# CREATE TABLE kv2 (
demo(# key text PRIMARY KEY,
demo(# accumulate boolean NOT NULL DEFAULT false,
demo(# value text
demo(# );
CREATE TABLE
demo=# INSERT INTO kv2 VAlUES
demo-# ('port', false, '5432'),
demo-# ('listen', true, NULL);
INSERT 0 2
demo=# SELECT * FROM kv2;
key | accumulate | value
--------+------------+-------
port | f | 5432
listen | t |
(2 rows)
Das WHERE
-Klausel kann verwendet werden, um entweder die „Wert“-Spalte zu überschreiben oder an sie anzuhängen, abhängig vom Wert von „Akkumulieren“, wie folgt:
demo=# INSERT INTO kv2 AS t (key, value) VALUES ('port', '3306')
demo-# ON CONFLICT (key) DO UPDATE SET value = concat_ws(',', t.value, EXCLUDED.value)
demo-# WHERE t.accumulate;
INSERT 0 0
demo=# INSERT INTO kv2 AS t (key, value) VALUES ('listen', '127.0.0.1')
demo-# ON CONFLICT (key) DO UPDATE SET value = concat_ws(',', t.value, EXCLUDED.value)
demo-# WHERE t.accumulate;
INSERT 0 1
demo=# INSERT INTO kv2 AS t (key, value) VALUES ('listen', '10.0.10.1')
demo-# ON CONFLICT (key) DO UPDATE SET value = concat_ws(',', t.value, EXCLUDED.value)
demo-# WHERE t.accumulate;
INSERT 0 1
demo=# SELECT * FROM kv2;
key | accumulate | value
--------+------------+---------------------
port | f | 5432
listen | t | 127.0.0.1,10.0.10.1
(2 rows)
Die erste Anweisung hat den Wert von „3306“ nicht in „Port“ akkumuliert, weil „Accumulate“ für diese Zeile deaktiviert war. Die nächsten beiden Anweisungen fügten die Werte „127.0.0.1“ und „10.0.10.1“ zum Wert von „listen“ hinzu, weil „accumulate“ wahr war.
Erzeugte Werte zurückgeben
Werte, die von PostgreSQL während des Einfügens generiert werden, wie Standardwerte oder automatisch inkrementierte SERIAL-Werte, können mit RETURNING
zurückgegeben werden -Klausel der INSERT-Anweisung.
Angenommen, Sie müssen zufällige UUIDs als Schlüssel für Zeilen in einer Tabelle generieren. Sie können PostgreSQL die Arbeit zum Generieren der UUIDs übernehmen lassen und den generierten Wert wie folgt an Sie zurückgeben:
demo=# INSERT INTO kv (key, value) VALUES (gen_random_uuid(), 'foo') RETURNING key;
key
--------------------------------------
d93ceaa5-30a8-4285-83c5-7defa79e2f90
(1 row)
INSERT 0 1
demo=# INSERT INTO kv (key, value) VALUES (gen_random_uuid(), 'bar') RETURNING key;
key
--------------------------------------
caf9c5d9-9a79-4b26-877f-a75a083b0c79
(1 row)
INSERT 0 1
demo=# SELECT * FROM kv;
key | value
--------------------------------------+-------
d93ceaa5-30a8-4285-83c5-7defa79e2f90 | foo
caf9c5d9-9a79-4b26-877f-a75a083b0c79 | bar
(2 rows)
Zeilen mit CTE-Klauseln verschieben
Sie können mit INSERT sogar Zeilen zwischen Tabellen verschieben, indem Sie WITH
verwenden Klausel.Hier sind zwei Tabellen mit Aufgabenlisten für verschiedene Jahre.
demo=# SELECT * FROM todos_2018;
what | done
----------------+------
thing to do #1 | t
thing to do #2 | t
thing to do #3 | f
(3 rows)
demo=# SELECT * FROM todos_2019;
what | done
------+------
(0 rows)
Um die Aufgaben, die 2018 noch nicht erledigt sind, nach 2019 zu verschieben, können Sie solche Zeilen grundsätzlich aus der 2018-Tabelle löschen und sie in einem Schritt in die 2019-Tabelle einfügen:
demo=# WITH items AS (
demo(# DELETE FROM todos_2018
demo(# WHERE NOT done
demo(# RETURNING *
demo(# )
demo-# INSERT INTO todos_2019 SELECT * FROM items;
INSERT 0 1
demo=# SELECT * FROM todos_2018;
what | done
----------------+------
thing to do #1 | t
thing to do #2 | t
(2 rows)
demo=# SELECT * FROM todos_2019;
what | done
----------------+------
thing to do #3 | f
(1 row)
Um mehr über die intelligente kleine INSERT-Anweisung zu erfahren, sehen Sie sich die Dokumentation an und experimentieren Sie!