Loading

ROUTE

ルートゼロの
アクティビティ

PostgreSQLのUPDATE FROMで別テーブルの値を更新

1

PostgreSQLで別テーブルの値を使って更新するには、UPDATEの後に更新先テーブル、SETに変更内容、FROMに参照元テーブル、WHEREに結合条件を書きます。実行前に最も注意したいのは、更新先1行に対して参照元が複数行一致しないこと。重複がある場合、どの値が使われるか予測できません。まずSELECTで結合結果を確認してから更新しましょう。

UPDATE FROMの基本構文と役割

UPDATE inventory AS i
SET unit_price = u.new_price
FROM price_updates AS u
WHERE i.sku = u.sku;

inventoryが更新先、price_updatesが参照元です。SETに更新する列、FROMに参照先のテーブル、WHEREに結合条件を置きます。WHEREを省くと想定外の行まで更新される可能性があるため、更新キーを明示してください。PostgreSQLのUPDATE構文です。MySQLのUPDATE JOINと同じ書き方ではありません。

用語解説:UPDATE … FROM
更新先の行と参照元テーブルの行を結合し、参照元の列を使って更新するPostgreSQLの構文です。結合の一致件数が更新結果に影響します。

根拠:PostgreSQL公式「UPDATE」https://www.postgresql.org/docs/current/sql-update.html


サンプルデータと更新後の期待値

商品マスターinventoryと価格更新データprice_updatesを例にします。各skuはprice_updates側で一意である前提です。データは説明用であり、実際のDBで自動実行した結果ではありません。

inventory.sku 更新前unit_price
A100 1000
B200 2000
C300 3000
price_updates.sku new_price
A100 1200
B200 1800
D400 900

結合条件が一致するA100とB200だけが更新対象です。C300は参照元に一致する行がないため、変更されません。D400は更新先に存在しません。

sku 更新後unit_price(期待値)
A100 1200
B200 1800
C300 3000

更新前にSELECTで対象行を確認する

いきなりUPDATEする前に、同じ結合条件をSELECTで確認します。WHEREで対象範囲が絞られているか、更新前後の値が正しいかをチェックしてください。

SELECT
    i.sku,
    i.unit_price AS before_price,
    u.new_price AS planned_price
FROM inventory AS i
JOIN price_updates AS u
  ON i.sku = u.sku
WHERE i.unit_price IS DISTINCT FROM u.new_price
ORDER BY i.sku;

IS DISTINCT FROMはNULLも含めて差分判定できるPostgreSQLの比較演算子です。WHERE句をUPDATEにも同じ形で反映すれば、すでに同額の商品を不要に更新する処理を避けられます。

UPDATE inventory AS i
SET unit_price = u.new_price
FROM price_updates AS u
WHERE i.sku = u.sku
  AND i.unit_price IS DISTINCT FROM u.new_price
RETURNING i.sku, i.unit_price;

RETURNINGは更新した行の値を返します。アプリのログや結果確認に役立ちます。ただしRETURNINGは変更を取り消す機能ではありません。


参照元に重複キーがあると危険な理由

例えばprice_updatesにA100が2件あり、それぞれ1200円と1300円を示している場合、どちらの値が使われるかは予測できません。PostgreSQLの公式マニュアルも、更新先1行に対してFROM側が1行しか一致しない設計を求めています。

SELECT sku, COUNT(*) AS matches
FROM price_updates
GROUP BY sku
HAVING COUNT(*) > 1;

この結果が1件でも出る場合は、そのままUPDATEしないでください。更新元を一意にするか、どの行を採用するかを明示的に決める必要があります。

  • skuに一意制約を設けられる場合は制約で保証する。
  • 履歴データなら更新日時と一意の連番で採用する1件を確定する。
  • 複数件が想定外ならデータ投入元を修正する。
  • DISTINCTだけで異なる価格の行を1件にまとめたつもりにならない。

サブクエリによるUPDATEとの使い分け

単一値を返すサブクエリでも別テーブルの値で更新できます。ただし、単純なSETサブクエリは一致する行がないとNULLを返すため、対象範囲をEXISTSなどで絞る必要があります。

UPDATE inventory AS i
SET unit_price = (
  SELECT u.new_price
  FROM price_updates AS u
  WHERE u.sku = i.sku
)
WHERE EXISTS (
  SELECT 1
  FROM price_updates AS u
  WHERE u.sku = i.sku
);

この形でサブクエリが複数行を返すとエラーになるため、参照元の重複に気付ける場合があります。対してUPDATE FROMでは複数一致時に不定の1件が使われ得ます。安全性や必要な処理に合わせて選びます。

トランザクションで変更を確認してから確定する

検証用DBではBEGINから試し、結果が想定と違えばROLLBACKできます。データを永続的に変更する必要があるときだけ、レビューと承認を経てCOMMITします。以下はロールバックする検証例です。

BEGIN;

UPDATE inventory AS i
SET unit_price = u.new_price
FROM price_updates AS u
WHERE i.sku = u.sku
  AND i.unit_price IS DISTINCT FROM u.new_price
RETURNING i.sku, i.unit_price;

ROLLBACK;

トランザクション中も行ロックやトリガーが関与することがあり、ROLLBACKで外部システム側のすべての副作用まで取り消せるとは限りません。検証環境で実施し、本番で実行するときは対象件数と運用手順を確認してください。

0件更新やNULLで迷ったときの確認項目

  • UPDATE 0ならskuの値・データ型・文字列の空白・WHERE条件を確認する。
  • 参照元に一致しない行はUPDATE FROMでは更新対象にならない。
  • NULLを含む価格差分は=や<>よりIS DISTINCT FROMで判定する。
  • 主キーや一意制約の有無を確認し、参照元の重複を事前に検出する。
  • 重要な更新は検証環境で対象行数と結果を比較してから本番へ適用する。

UPDATE FROMは一括更新に便利ですが、結合条件の誤りが大規模な誤更新へ直結します。SELECTで対象を確定し、変更差分と戻し方を事前に用意するのが基本です。

関連するSQL記事

JOIN条件の基本は既存記事で確認できます。更新式の中で条件分岐したい場合はCASE式の整理も役立ちます。

(結合条件と一致行数の確認については『SQL JOINの基本から実践まで|INNER・LEFT・RIGHT・OUTER JOINの違いをわかりやすく解説』をご参照ください)

(UPDATE時のCASE式については『SQL CASE式の使い方|SELECT・WHERE・UPDATEで条件分岐する書き方とつまずき解消実例』をご参照ください)

よくある質問

UPDATE FROMはUPDATE JOINと同じ書き方ですか?

考え方は似ていますが構文はDBごとに異なります。ここで紹介したのはPostgreSQLのUPDATE … FROMです。

参照元に同じキーが複数あるとどうなりますか?

更新先1行に対してFROM側が複数一致すると、どの行の値で更新するか予測できません。更新前に重複を確認してください。

RETURNINGが付いていれば安全に元へ戻せますか?

RETURNINGは更新した行の値を表示する機能で、取り消し機能ではありません。検証時はトランザクションとROLLBACKを使用します。

別テーブルの値で更新するときは「UPDATE先・FROM元・WHERE結合条件」を明示することに加え、更新先1行に対して参照元1行となることが重要です。更新前SELECT、重複キー検査、検証環境でのROLLBACKを組み合わせて安全に扱ってください。

実際の更新SQLを作る前に、まず同じ結合条件のSELECT結果を確認してみましょう。

カジュアル面談はこちら

RANKINGranking-icon

すべてを開示している会社です

ポジティブな面もネガティブな面も、包み隠さず誠実にお答えします。
気になることがあれば、面談の際にすべてオープンにお話します。