L'inserimento di una singola riga in una tabella è ciò che viene in mente quando si pensa all'istruzione INSERT in PostgreSQL. Tuttavia, ha qualche asso nella manica in più! Continua a leggere per scoprire alcune delle cose più interessanti che puoi fare con INSERT.
Copia in blocco
Supponiamo di voler acquisire periodicamente istantanee di una tabella:tutte le righe nella tabella devono essere copiate in un'altra tabella, con una colonna di timestamp aggiuntiva che indica quando è stata scattata l'istantanea. Ecco come creare e popolare la tabella la prima volta:
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)
E da quel momento in poi, puoi usare il INSERT..SELECT
forma di istruzione INSERT per copiare le righe da una tabella e inserirle in un'altra. Puoi anche inserire valori extra nella riga della tabella di destinazione.
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, il ON CONFLICT
la clausola è stata aggiunta a INSERT. Ciò consente agli sviluppatori di applicazioni di scrivere meno codice e svolgere più lavoro in SQL.
Ecco una tabella di coppie chiave e valore:
demo=# SELECT * FROM kv;
key | value
------+-----------
host | 127.0.0.1
port | 5432
(2 rows)
Un caso d'uso comune consiste nell'inserire una riga solo se non esiste e, in caso affermativo, non sovrascriverla. Questo viene fatto con ON CONFLICT..DO NOTHING
clausola dell'istruzione INSERT:
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)
Un altro uso comune è inserire una riga se non esiste e aggiornare il valore, se esiste. Questo può essere fatto con ON CONFLICT..DO UPDATE
clausola.
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)
Nel primo caso sopra il valore di 'host' è stato sovrascritto con il nuovo valore, nel secondo caso come terza riga è stato inserito il valore di 'ssl'.
Casi d'uso ancora più sofisticati possono essere realizzati con DO UPDATE
. Considera la tabella seguente, dove oltre alla chiave e al valore, c'è una colonna chiamata "accumula". Per le righe in cui accumulate è true, i valori devono essere accumulati come una stringa separata da virgole. Per le altre righe, i valori sono a valore singolo.
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)
Il WHERE
La clausola può essere utilizzata per sovrascrivere la colonna "valore" o per aggiungervi, a seconda del valore di "accumulare", in questo modo:
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)
La prima istruzione non ha accumulato il valore di "3306" in "porta" perché "accumula" era disattivato per quella riga. Le due affermazioni successive hanno aggiunto i valori "127.0.0.1" e "10.0.10.1" al valore di "ascolta", perché "accumula" era vero.
Restituzione di valori generati
I valori generati da PostgreSQL durante l'inserimento, come i valori predefiniti oi valori SERIAL incrementati automaticamente possono essere restituiti utilizzando il RETURNING
clausola dell'istruzione INSERT.
Si supponga di dover generare UUID casuali come chiavi per le righe in una tabella. Puoi lasciare che PostgreSQL esegua il lavoro di generazione degli UUID e ti restituisca il valore generato in questo modo:
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)
Spostamento di righe con clausole CTE
Puoi anche spostare le righe tra le tabelle con INSERT, usando il WITH
clausola.Ecco due tabelle con elenchi di cose da fare per anni diversi.
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)
Per spostare le cose da fare che non sono state ancora completate nel 2018 nel 2019, puoi sostanzialmente eliminare tali righe dalla tabella 2018 e inserirle nella tabella 2019 in un colpo solo:
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)
Per saperne di più sulla piccola e intelligente dichiarazione INSERT, dai un'occhiata alla documentazione e sperimenta!