次は機能しますか?
WITH upd AS (
UPDATE main_stat t
SET counter = s.counter
FROM tmp_stat s
WHERE t.date = s.date
AND t.site_id = s.site_id
AND t.ip = s.ip
AND t.block_id = s.block_id
RETURNING s.date, s.site_id, s.ip, s.block_id, s.counter
)
INSERT INTO main_stat
SELECT s.mydate, s.site_id, s.ip, s.block_id, s.counter
FROM tmp_stat s
LEFT JOIN upd ON (upd.date = s.date and upd.site_id = s.site_id and upd.ip = s.ip and upd.block_id = s.block_id)
WHERE upd.date IS NULL
;
アップデート:
これはバージョン9.1以降でのみ使用できるようです。
誰かの提案を使用WHERE (t.date, t.site_id, t.ip, t.block_id) = (s.date, s.site_id, s.ip, s.block_id)
すると、パフォーマンスが向上するようです。
WITH upd AS (
UPDATE main_stat t
SET counter = s.counter
FROM tmp_stat s
WHERE ( t.date, t.site_id, t.ip, t.block_id ) = ( s.date, s.site_id, s.ip, s.block_id )
RETURNING s.date, s.site_id, s.ip, s.block_id
)
INSERT INTO main_stat
SELECT s.date, s.site_id, s.ip, s.block_id, s.counter
FROM tmp_stat s
LEFT JOIN upd
ON ( upd.date = s.date
AND upd.site_id = s.site_id
AND upd.ip = s.ip
AND upd.block_id = s.block_id )
WHERE upd.date IS NULL
;
ここで起こっていることは、CTEを使用してUPDATEを実行し、CTEが更新された行の識別列を返すことです。
次に、INSERTは更新された行情報を使用してtmp_statをフィルタリングし、新しいレコードのみを挿入します。
DimitriFontaineがこのブログエントリでカバーしているいくつかの同時実行の警告があります。
CTEの詳細については、Postgresqlのドキュメントを参照してください。