Wstawienie pojedynczego wiersza do tabeli jest tym, co przychodzi na myśl, gdy myślisz o instrukcji INSERT w PostgreSQL. Ma jednak w zanadrzu kilka dodatkowych sztuczek! Czytaj dalej, aby odkryć niektóre z bardziej interesujących rzeczy, które możesz zrobić za pomocą INSERT.
Kopiowanie zbiorcze
Załóżmy, że chcesz okresowo przechwytywać migawki tabeli — wszystkie wiersze w tabeli powinny zostać skopiowane do innej tabeli z dodatkową kolumną sygnatury czasowej, wskazującą, kiedy wykonano migawkę. Oto, jak możesz utworzyć i wypełnić tabelę za pierwszym razem:
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)
Od tego momentu możesz używać INSERT..SELECT
forma instrukcji INSERT, aby skopiować wiersze z jednej tabeli i wstawić do drugiej. Możesz również wpisać dodatkowe wartości w wierszu tabeli docelowej.
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)
Przewrotki
W PostgreSQL 9.5, ON CONFLICT
klauzula została dodana do INSERT. Pozwala to programistom aplikacji pisać mniej kodu i wykonywać więcej pracy w SQL.
Oto tabela par kluczy i wartości:
demo=# SELECT * FROM kv;
key | value
------+-----------
host | 127.0.0.1
port | 5432
(2 rows)
Typowym przypadkiem użycia jest wstawienie wiersza tylko wtedy, gdy nie istnieje – a jeśli tak, nie nadpisuj. Odbywa się to za pomocą ON CONFLICT..DO NOTHING
klauzula instrukcji 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)
Innym powszechnym zastosowaniem jest wstawienie wiersza, jeśli nie istnieje, i zaktualizowanie wartości, jeśli tak. Można to zrobić za pomocą ON CONFLICT..DO UPDATE
klauzula.
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)
W pierwszym przypadku powyżej wartość „host” została zastąpiona nową wartością, a w drugim przypadku wartość „ssl” została wstawiona jako trzeci wiersz.
Jeszcze bardziej wyrafinowane przypadki użycia można zrealizować za pomocą DO UPDATE
. Rozważ poniższą tabelę, w której oprócz klucza i wartości znajduje się kolumna o nazwie „akumuluj”. W przypadku wierszy, w których akumulacja ma wartość prawda, wartości mają być akumulowane jako ciąg oddzielony przecinkami. W przypadku innych wierszy wartości są jednowartościowe.
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)
WHERE
Klauzula może być użyta do nadpisania kolumny „value” lub dołączyć do niej, w zależności od wartości „accumulate”, w ten sposób:
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)
Pierwsze stwierdzenie nie skumulowało wartości „3306” do „port”, ponieważ „akumuluj” było wyłączone dla tego wiersza. Następne dwa stwierdzenia dodały wartości „127.0.0.1” i „10.0.10.1” do wartości „słuchaj”, ponieważ „akumuluj” było prawdziwe.
Zwracanie wygenerowanych wartości
Wartości generowane przez PostgreSQL podczas wstawiania, takie jak wartości domyślne lub wartości z autoinkrementacją SERIAL mogą być zwracane za pomocą RETURNING
klauzula instrukcji INSERT.
Załóżmy, że musisz wygenerować losowe identyfikatory UUID jako klucze dla wierszy w tabeli. Możesz pozwolić PostgreSQL na wykonanie pracy polegającej na generowaniu identyfikatorów UUID i zwróceniu wygenerowanej wartości w ten sposób:
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)
Przenoszenie wierszy z klauzulami CTE
Możesz nawet przenosić wiersze między tabelami za pomocą INSERT, używając WITH
klauzula.Oto dwie tabele z listami rzeczy do zrobienia dla różnych lat.
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)
Aby przenieść elementy do zrobienia, które nie zostały jeszcze ukończone w 2018 r. na 2019 r., możesz zasadniczo usunąć takie wiersze z tabeli 2018 i wstawić je do tabeli 2019 za jednym razem:
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)
Aby dowiedzieć się więcej o sprytnym, małym oświadczeniu INSERT, zapoznaj się z dokumentacją i eksperymentuj!