3列の_stock_price_alert
_テーブルがあります。 _stock_price_id
_は_PRIMARY KEY
_であり、他のテーブルに対しても_FOREIGN KEY
_です。以下のテーブル定義:
_create table stock_price_alert (
stock_price_id integer references stock_price (id) on delete cascade not null,
fall_below_alert boolean not null,
rise_above_alert boolean not null,
primary key (stock_price_id)
);
_
次のいずれかが必要です。
1)INSERT
レコード(存在しない場合)
_-- query 1
INSERT INTO stock_price_alert (stock_price_id, fall_below_alert, rise_above_alert)
VALUES (1, true, false);
_
2)UPDATE
レコード(存在する場合)
_-- query 2
UPDATE stock_price_alert SET
fall_below_alert = true,
rise_above_alert = false
WHERE stock_price_id = 1;
_
最初に、クエリ(1)または(2)のどちらを実行するかを決定するために、SELECT
クエリを_stock_price_alert
_テーブルで発行する必要があります。
Postgresは_INSERT INTO TABLE .... ON CONFLICT DO UPDATE ...
_をサポートしています:
_-- query 3
INSERT INTO stock_price_alert (stock_price_id, fall_below_alert, rise_above_alert)
VALUES (1, true, false)
ON CONFLICT (stock_price_id) DO UPDATE SET
fall_below_alert = EXCLUDED.fall_below_alert,
rise_above_alert = EXCLUDED.rise_above_alert;
_
クエリ(1)または(2)を使用する代わりに、常にクエリ(3)を使用できますか?それから、事前にSELECT
クエリを発行する必要がなく、コードを簡素化するのに役立ちます。
しかし、私は疑問に思っています、どれがベストプラクティスですか?クエリ(3)はパフォーマンスの問題や望ましくない副作用を引き起こしますか?ありがとう。
クエリ3は、Postgres 9.5で導入された "UPSERT"(= UPDATEまたはINSERT)のPostgres構文です。
ドキュメント から:
ON CONFLICT DO UPDATE
は、アトミックなINSERT
またはUPDATE
の結果を保証します。独立したエラーがない限り、これらの2つの結果のいずれかは、同時実行性が高い場合でも保証されます。これは、UPSERT
–“UPDATE
またはINSERT
”とも呼ばれます。
これは、達成しようとしているもののベストプラクティスです。
ON CONFLICTに加えてWHERE NOT EXISTSを使用すると、INSERTS(まだUPSERTSをテストしていない)の方がはるかに高速であることに気付きました。通常、ON CONFLICTによる存在チェックの処理を許可するよりも約3倍高速です。これはUPSERTSに持ち越され、INSERTを実行してからUPDATEを実行する方が高速になる可能性があると思います。挿入のみのテストです...
--so i can keep rerunning
DROP TABLE if exists temp1;
DROP TABLE if exists temp2;
--create a billion rows
SELECT GENERATE_SERIES AS id INTO TEMP temp1
FROM GENERATE_SERIES(1, 10000000);
CREATE UNIQUE INDEX ux_id ON temp1(id);
ALTER TABLE temp1 CLUSTER ON ux_id;
--create a second table to insert from, with the same data
SELECT * INTO TEMP temp2
FROM temp1;
CREATE UNIQUE INDEX ux_id2 ON temp2(id);
ALTER TABLE temp2 CLUSTER ON ux_id2;
--test inserting with on conflict only
INSERT INTO temp1(id)
SELECT id
FROM temp2 ON conflict DO nothing;
--execution time: 14.71s (1million rows)
--test inserting with not exists and on conflict
INSERT INTO temp1(id)
SELECT t2.id
FROM temp2 t2
WHERE NOT EXISTS (SELECT 1 FROM temp1 t1 WHERE t2.id = t1.id)
ON conflict DO nothing;
--execution time: 5.78s (1million rows)