PostgreSQL Oracle MERGEのような機能の実装
2436 ワード
文書ディレクトリ
概要
MERGE文は、UPDATE文とINSERT文を統合するOracle 9 iの新しい構文です.MERGE文では、1つのテーブルまたはサブクエリの接続条件に基づいて別のテーブルをクエリし、接続条件マッチング上のUPDATEを行い、マッチングできないINSERTを実行します.この構文は1回のフルテーブルスキャンですべての作業を完了し、実行効率はINSERT+UPDATEより高い.
PostgreSQLではこの構文は直接サポートされていませんが、PostgreSQLはWITH Queries(Common Table Expressions)の方法で同じ機能を実現できます.
では、一緒に見てみましょう.
文の詳細
次のSQLはtest 2テーブルのデータmergeをtest 1テーブルに、プライマリ・キー・フィールドがidである
WITH upsert AS (
UPDATE test1
SET col1 = test2.col1
FROM test2
WHERE test1.id = test2.id
RETURNING test1.*
)
INSERT INTO test01
SELECT *
FROM test2
WHERE NOT EXISTS (
SELECT 1
FROM upsert b
WHERE test2.id = b.id
);
実はこのSQLのポイントはpostgresqlのupdate特性--RETURNINGを利用して、updateの結果セットを返します.クエリー条件が存在するため(プライマリ・キーであるため、唯一です)、2つのテーブルが重なる部分をフィルタして、where not existsでこれらの重なる部分を無視します.これでデータメッセージが入ります
小テスト
表を2枚作る
postgres=# create table test1(id int primary key,name text);
CREATE TABLE
postgres=#
postgres=# create table test2(id int primary key,name text);
CREATE TABLE
データ部分重複
postgres=# select * from test1;
id | name
----+-------
1 | aaaaa
2 | aaaaa
3 | aaaaa
4 | aaaaa
5 | aaaaa
(5 rows)
postgres=# select * from test2;
id | name
----+-------
4 | aaaaa
5 | aaaaa
6 | bbbbb
7 | bbbbb
8 | bbbbb
9 | bbbbb
(6 rows)
merge文の実行
test 2という表でtest 1を更新すると、test 1にないデータが挿入され、変更されないものもあります
postgres=# WITH upsert AS (
UPDATE test1
SET name = test2.name
FROM test2
WHERE test1.id = test2.id
RETURNING test1.*
)
INSERT INTO test1
SELECT *
FROM test2
WHERE NOT EXISTS (
SELECT 1
FROM upsert b
WHERE test2.id = b.id
);
INSERT 0 4
postgres=# select * from test1;
id | name
----+-------
1 | aaaaa
2 | aaaaa
3 | aaaaa
4 | aaaaa
5 | aaaaa
6 | bbbbb
7 | bbbbb
8 | bbbbb
9 | bbbbb
(9 rows)
データが更新されていることがわかります
一つの注意点
私の実際のビジネスシーンの更新では、お客様のtest 2テーブル、ID列がプライマリ・キーではなく、ID列がどれだけ重複しているかを確認する重複SQLがたくさんあることに注意しなければならない点を発見しました.0の場合、重複値はありません.
select count(*) from users_purse where id in (select id from users_purse group by id having COUNT(*)>1)
このような状況に遭遇すると、test 1のため、エラーが発生する可能性があります.IDは重複できないので、distinctを再処理する必要がある可能性があります.