Menyisipkan satu baris ke dalam tabel adalah hal yang terlintas di benak Anda saat memikirkan pernyataan INSERT di PostgreSQL. Namun, ia memiliki beberapa trik lagi! Baca terus untuk mengetahui beberapa hal menarik yang dapat Anda lakukan dengan INSERT.
Menyalin secara Massal
Katakanlah Anda ingin mengambil snapshot tabel secara berkala – semua baris dalam tabel harus disalin ke tabel lain, dengan kolom stempel waktu tambahan yang menunjukkan kapan snapshot diambil. Berikut cara membuat dan mengisi tabel untuk pertama kalinya:
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)
Dan sejak saat itu, Anda dapat menggunakan INSERT..SELECT
bentuk pernyataan INSERT untuk menyalin baris dari satu tabel dan menyisipkan ke tabel lain. Anda juga dapat mengisi nilai tambahan ke dalam baris tabel tujuan.
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
Di PostgreSQL 9.5, ON CONFLICT
klausa telah ditambahkan ke INSERT. Ini memungkinkan pengembang aplikasi menulis lebih sedikit kode dan melakukan lebih banyak pekerjaan di SQL.
Berikut adalah tabel pasangan kunci, nilai:
demo=# SELECT * FROM kv;
key | value
------+-----------
host | 127.0.0.1
port | 5432
(2 rows)
Kasus penggunaan yang umum adalah menyisipkan baris hanya jika tidak ada – dan jika ada, jangan ditimpa. Ini dilakukan dengan ON CONFLICT..DO NOTHING
klausa dari pernyataan 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)
Penggunaan umum lainnya adalah menyisipkan baris jika tidak ada, dan memperbarui nilainya, jika ada. Ini dapat dilakukan dengan ON CONFLICT..DO UPDATE
klausa.
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)
Dalam kasus pertama di atas nilai 'host' ditimpa dengan nilai baru, dan dalam kasus kedua nilai 'ssl' dimasukkan sebagai baris ketiga.
Kasus penggunaan yang lebih canggih dapat diwujudkan dengan DO UPDATE
. Perhatikan tabel di bawah ini, dimana selain key dan value, ada kolom yang disebut “accumulate”. Untuk baris di mana akumulasi benar, nilainya dimaksudkan untuk terakumulasi sebagai string yang dipisahkan koma. Untuk baris lain, nilainya bernilai tunggal.
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
klausa dapat digunakan untuk menimpa kolom “nilai”, atau menambahkan ke dalamnya, tergantung pada nilai “akumulasi”, seperti ini:
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)
Pernyataan pertama tidak mengakumulasikan nilai '3306' ke dalam 'port' karena 'accumulate' tidak aktif untuk baris itu. Dua pernyataan berikutnya menambahkan nilai '127.0.0.1' dan '10.0.10.1' ke dalam nilai 'listen', karena 'accumulate' adalah benar.
Mengembalikan Nilai yang Dihasilkan
Nilai yang dihasilkan oleh PostgreSQL selama penyisipan, seperti nilai default atau nilai SERIAL yang bertambah otomatis dapat dikembalikan menggunakan RETURNING
klausa pernyataan INSERT.
Asumsikan Anda perlu membuat UUID acak sebagai kunci untuk baris dalam tabel. Anda dapat membiarkan PostgreSQL melakukan pekerjaan menghasilkan UUID dan mengembalikan nilai yang dihasilkan kepada Anda seperti ini:
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)
Memindahkan Baris dengan Klausa CTE
Anda bahkan dapat memindahkan baris antar tabel dengan INSERT, menggunakan WITH
klausa.Berikut adalah dua tabel dengan daftar tugas untuk tahun yang berbeda.
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)
Untuk memindahkan item agenda yang belum selesai pada 2018 ke 2019, Anda pada dasarnya dapat menghapus baris tersebut dari tabel 2018 dan memasukkannya ke dalam tabel 2019 dalam satu kesempatan:
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)
Untuk mempelajari lebih lanjut tentang pernyataan INSERT kecil yang cerdas, lihat dokumentasi dan eksperimen!