昔の記事の関連みたいなもの。
【PostgreSQL】レコードが存在すればUPDATE、なければINSERTをSQL文で実現する
最近っぽいやり方で1クエリで実現する。
※PostgreSQLのバージョンが9.5以上でないと使えないはず。
環境:
- PostgreSQL:9.6.3
例として、テーブル定義は下記の状態とする。
# \d guests Table "public.guests" Column | Type | Modifiers ----------+------------------------+----------- name | character varying(255) | email | character varying(255) | not null password | character varying(255) | Indexes: "guests_pkey" PRIMARY KEY, btree (email)
emailの有無によりINSERTかUPDATEかを判別するという想定。
テーブルの内容は下記。
# SELECT * FROM guests; name | email | password ------+-------+---------- aaaa | a@a.a | aaa bbbb | b@b.b | bbb (2 rows)
emailがPRIMARY KEYの為、当然emailに存在する値をINSERTしようとするとエラーとなる。
# INSERT INTO guests (name, email, password) VALUES ('cccc', 'a@a.a', 'ccc'); ERROR: duplicate key value violates unique constraint "guests_pkey" DETAIL: Key (email)=(a@a.a) already exists.
そこで、下記のSQL文とする。
# INSERT INTO guests (name, email, password) VALUES ('cccc', 'a@a.a', 'ccc') # ON CONFLICT (email) DO UPDATE SET name = 'cccc', password = 'ccc'; INSERT 0 1
2行目がカギで、emailが重複していた場合はnameとpasswordをUPDATE、していなければINSERT、となる。
実行後のテーブルの状態は下記。
# SELECT * FROM guests; name | email | password ------+-------+---------- bbbb | b@b.b | bbb cccc | a@a.a | ccc (2 rows)
当然、emailが重複していなければ普通にINSERTが行われる。
# INSERT INTO guests (name, email, password) VALUES ('dddd', 'd@d.d', 'ddd') # ON CONFLICT (email) DO UPDATE SET name = 'dddd', password = 'ddd'; INSERT 0 1 # SELECT * FROM guests; name | email | password ------+-------+---------- bbbb | b@b.b | bbb cccc | a@a.a | ccc dddd | d@d.d | ddd (3 rows)
注意点として、CONFLICTに指定するカラムはUNIQUEである必要がある。
通常のカラムの場合は動作しない為注意。
# \d admin Table "public.admin" Column | Type | Modifiers ----------+------------------------+----------- name | character varying(255) | email | character varying(255) | password | character varying(255) | # SELECT * FROM admin; name | email | password ------+-------+---------- aaaa | a@a.a | aaa bbbb | b@b.b | bbb (2 rows) # INSERT INTO admin (name, email, password) VALUES ('cccc', 'a@a.a', 'ccc') ON CONFLICT (email) DO UPDATE SET name = 'cccc', password = 'ccc'; ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification