PostgreSQL に馴染みがなく、アップサートあるいは、SERIAL に困惑している。
MySQL には、AUTO INCREMENT があり、例えば、以下のようなテーブル
CREATE TABLE sample ( id INT NOT NULL AUTO_INCREMENT, point INT, price INT, PRIMARY KEY (id) ) ENGINE=InnoDB AUTO_INCREMENT=1
これと同等な PostgreSQL でのテーブルは、
CREATE TABLE sample ( id SERIAL NOT NULL, point INT, price INT, CONSTRAINT sample_pkey PRIMARY KEY (id) ) 制約を見る。。 SELECT table_name, constraint_name, constraint_type FROM information_schema.table_constraints WHERE table_name='sample'; table_name | constraint_name | constraint_type ------------+-----------------------+----------------- sample | sample_pkey | PRIMARY KEY sample | 2200_16824_1_not_null | CHECK
まず、MySQLの AUTO INCREMENT 同様、PostgreSQL の 動き、
SELECT * FROM sample; id | point | price ----+-------+------- 1 | 10 | 100 2 | 20 | 200 (2 行)
ここで、、、INSERT INTO sample (point, price) VALUES (30, 300)
を実行する。MySQL も同じく、PostgreSQL でも、
SELECT * FROM sample; id | point | price ----+-------+------- 1 | 10 | 100 2 | 20 | 200 3 | 30 | 300 (3 行)
ここまでは、別に違和感もなく特になにもなく納得。
アップサートを行う!!
まず、存在するレコードに対して、、、
MySQL では、以下のように書く。
INSERT INTO sample (id, `point`, price) VALUES (3, 40, 400) ON DUPLICATE KEY UPDATE `point` = VALUES(`point`) , price = VALUES(price)
PostgreSQL では、以下のように書く
INSERT INTO sample (id, `point`, price) VALUES (3, 40, 400) ON CONFLICT ON CONSTRAINT sample_pkey DO UPDATE SET point = 40, price = 400
どちらも、id = 3 のレコードだけが更新される。
実践では、動的SQL文生成でこのアップサート文を挿入か更新か処理設計の都合で
実行時にならないと定まらないまま使う。
「アップサート」という本来の目的からすれば、挿入か更新か実行時に判断されることを
期待している。
だから、MySQL では、アップサートが以下のように、制約キー id が NULL の時、、
INSERT INTO sample (id, `point`, price) VALUES (null, 40, 400) ON DUPLICATE KEY UPDATE `point` = VALUES(`point`) , price = VALUES(price)
これは、更新でなく新しいレコードが挿入される。AUTO INCREMENT が効いてくる。
しかし、、、PostgreSQL では、、
INSERT INTO sample (id, `point`, price) VALUES (null, 40, 400) ON CONFLICT ON CONSTRAINT sample_pkey DO UPDATE SET point = 40, price = 400 ERROR: 列"id"内のNULL値はNOT NULL制約違反です
エラーになり、新しいレコード挿入にならない!!・・・あれ?上に書いたように、
INSERT INTO sample (point, price) VALUES (30, 300)
が成功する PostgreSQL に AUTO INCREMENT の代わりとされてる SERIAL は、何なの?
PostgreSQL のアップサート文で以下のようにすると成功する。
INSERT INTO sample (id, point, price) VALUES (nextval('sample_id_seq'::regclass), 40, 400) ON CONFLICT ON CONSTRAINT sample_pkey DO UPDATE SET point = 40, price = 400
これじゃ、実行するSQL生成として、 VALUES 文に、null を入れる代わりに、
nextval('sample_id_seq'::regclass) を入れるなんて、変なことをしなくてはならない。
挿入でも更新でも1つのアップサートSQLでという本来の目的に合わない!
実践の処理として考えると既存データを編集した登録のSQLは、制約キーIDの値が存在するデータで
SQL文にパラメータ渡しで生成するであろうし、
新しいデータである場合、NULL をSQL文生成に渡すことになる。この実行時にならないと
決定しない時に、アップサートのSQL文が威力を発揮するはずで、
PostgreSQL のアップサートとされている文は、動的変化に耐えられない。
PostgreSQL より、MySQL の方に軍配が上がる。
そもそも、そんなDBの比較なんて無意味なのか。。。