2016年1月7日(米国時間)にPostgreSQLの最新版、バージョン9.5がリリースされました。
ここ数年は、主にレプリケーションや外部テーブルに関連して多くの機能強化が図られており、今回もその例にもれませんが、INSERTやUPDATEといった基本的なSQLでも機能強化があります。
以下のリリース発表(英語版)で主な新機能について紹介されています。
http://www.postgresql.org/about/news/1636/
PostgreSQL 9.5での基本機能の強化や変更について、2回に分けて紹介します。
なお、変更点についてはすべてリリースノートに記載されていますので、詳しくはリリースノートを参照してください。
http://www.postgresql.org/docs/9.5/static/release-9-5.html(英語)
http://www.postgresql.jp/document/current/html/release-9-5.html(日本語)
(1)pg_ctl stop でのデフォルト動作の変更
pg_ctl stop でデータベースを停止するとき、停止の動作について-mでsmart/fast/immediateの3通りのオプションが指定できる部分は従来通りですが、-mオプションを指定しなかった時のデフォルトの動作が変更されました。
PostgreSQL 9.4まではsmartがデフォルトでしたが、9.5ではfastがデフォルトになりました。
つまり、従来はデータベース接続中のセッションがあれば、それが終了するまで待機するのがデフォルトでしたが、9.5からは強制的にセッションを切断して終了するのがデフォルトとなります。
実運用のシステムにデータベース停止のスクリプトが組み込まれている場合、停止のオプションを指定しているかどうか、確認しておきましょう。デフォルトの動作を使う場合でも、明示的にオプションを指定しておけば、アップグレード時にデフォルトが変わっても影響を受けないので、そのような習慣をつけておくのも良いでしょう。
(2)INSERTでUPSERT機能の追加
UPSERTはINSERT+UPDATEからの造語で、テーブルに同じ主キーのレコードがなければ普通にINSERTを実行、同じ主キーのレコードが既に存在するなら、それをUPDATEする、という処理のことです。
これ自体は広く使われている用語で、他の主要なRDBMSでもそれに相当する機能がサポートされているものがありますが、実際にUPSERTというコマンドが使われるわけではなく、MERGEコマンド、REPLACEコマンド、INSERTにオプション句を追加、などRDBMSの種類によって実装の方法は様々です。
PostgreSQL 9.5ではINSERTにオプションのON CONFLICT句を追加することでUPSERT機能をサポートしました。
具体的な構文は
INSERT INTO ...
ON CONFLICT [conflict_target] conflict_action
です。
conflict_actionとして、
DO NOTHING
または
DO UPDATE SET ...
を記述することで、主キー制約などに反するINSERTを無視する(DO NOTHING)あるいはUPDATEに変更する(DO UPDATE)ことができます。なお、DO UPDATEを指定する場合は、一意キー列の列名をconflict_targetに記述します。
以下に具体的な例を示します。
(青:psqlのプロンプトからの入力、緑:psqlからの出力)
同じ構造のテーブルtest1, test2があり、id列が主キーに設定されているものとします。
select * from test1;
id | val
----+-----
1 | aaa
2 | bbb
3 | ccc
(3 rows)
select * from test2;
id | val
----+-----
3 | XXX
4 | YYY
5 | ZZZ
(3 rows)
test2の内容を単純なINSERTでtest1にコピーしようとすると、id=3の行が重複するため、
insert into test1 (id, val)
select id, val from test2;
ERROR: duplicate key value violates unique constraint "test1_pkey"
DETAIL: Key (id)=(3) already exists.
という具合にエラーになります。
ON CONFLICT DO NOTHINGを使うと、重複行は無視されて、エラーが発生しなくなります。
insert into test1 (id, val)
select id, val from test2
on conflict do nothing;
INSERT 0 2
select * from test1;
id | val
----+-----
1 | aaa
2 | bbb
3 | ccc
4 | YYY
5 | ZZZ
(5 rows)
ON CONFLICT DO UPDATEを使うと、UPSERT機能が実現できます。
insert into test1 (id, val)
select id, val from test2
on conflict (id) do update set val = excluded.val;
INSERT 0 3
select * from test1;
id | val
----+-----
1 | aaa
2 | bbb
3 | XXX
4 | YYY
5 | ZZZ
(5 rows)
INSERTしようとして、制約のためON CONFLICTに回されたデータは、SET句の中から仮想的なexcludedテーブルとして参照できます。
後半に続きます。
解説:松田神一
© EDUCO All Rights Reserved.