テーブルに単一の行を挿入することは、PostgreSQLのINSERTステートメントについて考えるときに頭に浮かぶことです。ただし、袖にはさらにいくつかのトリックがあります。 INSERTを使用して実行できる、より興味深いことのいくつかを見つけるために読んでください。
テーブルのスナップショットを定期的にキャプチャするとします。テーブル内のすべての行を別のテーブルにコピーし、スナップショットが作成された日時を示す追加のタイムスタンプ列を追加する必要があります。初めてテーブルを作成してデータを入力する方法は次のとおりです。
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)
それ以降は、INSERT..SELECT
を使用できます。 あるテーブルから行をコピーして別のテーブルに挿入するINSERTステートメントの形式。宛先テーブルの行に追加の値を入力することもできます。
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)
PostgreSQL 9.5では、ON CONFLICT
INSERTに句が追加されました。これにより、アプリケーション開発者はより少ないコードを記述し、SQLでより多くの作業を行うことができます。
キーと値のペアの表は次のとおりです。
demo=# SELECT * FROM kv;
key | value
------+-----------
host | 127.0.0.1
port | 5432
(2 rows)
一般的な使用例は、行が存在しない場合にのみ行を挿入することです。存在する場合は、上書きしないでください。これは、ON CONFLICT..DO NOTHING
を使用して行われます。 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)
もう1つの一般的な使用法は、行が存在しない場合は行を挿入し、存在する場合は値を更新することです。これは、ON CONFLICT..DO UPDATE
を使用して実行できます。 条項。
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)
上記の最初のケースでは、「host」の値が新しい値で上書きされ、2番目のケースでは、「ssl」の値が3番目の行として挿入されました。
DO UPDATE
を使用すると、さらに高度なユースケースを実現できます。 。以下の表を検討してください。ここでは、キーと値に加えて、「accumulate」という列があります。 Accumulateがtrueの行の場合、値はコンマ区切りの文字列として累積されることを意味します。他の行の場合、値は単一値です。
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
句は、次のように、「accumulate」の値に応じて、「value」列を上書きするか、列に追加するために使用できます。
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)
最初のステートメントは、「accumulate」がその行でオフになっているため、「3306」の値を「port」に累積しませんでした。次の2つのステートメントは、「accumulate」がtrueであったため、「listen」の値に「127.0.0.1」と「10.0.10.1」の値を追加しました。
デフォルト値や自動インクリメントされたSERIAL値など、挿入中にPostgreSQLによって生成された値は、RETURNING
を使用して返すことができます。 INSERTステートメントの句。
テーブルの行のキーとしてランダムなUUIDを生成する必要があると想定します。 PostgreSQLにUUIDを生成する作業を行わせ、次のように生成された値を返してもらうことができます。
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)
CTE句を使用した行の移動
WITH
を使用して、INSERTを使用してテーブル間で行を移動することもできます。 条項。これは、異なる年のToDoリストを含む2つの表です。
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)
2018年から2019年にまだ完了していないToDoアイテムを移動するには、基本的にそのような行を2018テーブルから削除し、それらを2019テーブルにワンショットで挿入できます:
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)
スマートな小さなINSERTステートメントの詳細については、ドキュメントと実験を確認してください!