Q1UPDATE stock SET qty = qty + (SELECT SUM(add_qty) FROM stock_in WHERE stock_in.sku = stock.sku);をWHERE EXISTS (...)を付けずに実行したとき、stock_inに対応行が無い sku のqtyはどうなりますか。
サブクエリ・JOIN を使う UPDATE / DELETE
この記事は、基礎から複雑なSQL,SQLチューニングまでSQLの実践的なスキルを1からマスターする「SQL入門講座の一部」です。
在庫テーブルstockと入庫テーブルstock_inを題材に、相関サブクエリでの一括更新、UPDATE … FROMの結合更新、サブクエリ条件付きDELETEと影響行数の確認までを実際に動かして学びます。
本記事で使うデータ — stock と stock_in
本記事では、別のテーブルや集計結果を使って更新・削除する一段高度な書き方を扱います。
具体的には、相関サブクエリ(外側の行ごとに評価される、内側の SELECT が外側の列を参照するサブクエリ)で値を計算する更新、UPDATE … FROMによる別テーブルとの結合更新、サブクエリをWHEREに置くDELETEの 3 つです。
相関サブクエリで別テーブルの値を使って更新する
UPDATE 表 SET 列 = (SELECT ... WHERE サブクエリ側.キー = 表.キー)と書くと、更新対象の行ごとに内側の SELECT が評価され、その結果で列を書き換えられます。
内側の SELECT が外側のUPDATE対象の列(stock.sku)を参照するため、これを相関サブクエリと呼びます。
注意点は、stock_inに対応行が無いstockの行では内側の SELECT が NULL を返すことです。
-- COALESCE を使わない例: 対応行が無い sku の qty が NULL で上書きされる
UPDATE stock
SET qty = qty + (SELECT SUM(add_qty) FROM stock_in WHERE stock_in.sku = stock.sku);
qty = qty + (SELECT ...)のまま実行すると、入庫が無い行のqtyが NULL で上書きされてしまいます。
これを防ぐため、COALESCE(サブクエリ, 0)で NULL を 0 に変換するか、WHERE EXISTS (...)で「入庫がある行だけ」に対象を絞ります。
-- まず SELECT で更新後の値を確認(実行しても stock は変わらない)
SELECT sku, qty,
qty + (SELECT COALESCE(SUM(add_qty), 0) FROM stock_in WHERE stock_in.sku = stock.sku) AS new_qty
FROM stock;
-- 想定どおりなら UPDATE に書き換え(COALESCE で NULL を 0 に変換)
UPDATE stock
SET qty = qty + (SELECT COALESCE(SUM(add_qty), 0) FROM stock_in WHERE stock_in.sku = stock.sku);
UPDATE … FROM で別テーブルと結合して更新する
UPDATE 表 SET 列 = 値 FROM 別テーブル WHERE 結合条件と書くと、UPDATEの対象表とFROMで指定した別テーブルを結合してから更新でき、結合先の列をそのままSETに書けます。
-- UPDATE … FROM の例: 入庫予定のある商品だけ価格を 10 上げる
UPDATE stock
SET price = price + 10
FROM stock_in
WHERE stock.sku = stock_in.sku;
-- 結合できた sku(A001 / A004)だけ price が上がる
SELECT sku, price FROM stock ORDER BY sku;
FROM側のstock_inに対応行が無いstockの行は、WHEREの結合条件が成立せず更新対象から自動的に外れます。
相関サブクエリのように NULL 化を別途防ぐ必要がない点が利点です。
サブクエリ条件の DELETE と影響行数の確認
DELETE FROM 表 WHERE 列 IN (SELECT ...)やWHERE EXISTS (SELECT ...)のように、DELETEのWHEREにもサブクエリを置けます。
これで「別テーブルに存在する/しない行だけを削除する」「集計結果が条件を満たす行を削除する」といった、固定値では表せない削除ができます。
削除は元に戻せない破壊的な操作なので、本番運用では先に同じWHEREでSELECT COUNT(*)を実行して影響行数を確認してからDELETEに書き換えるのが基本動作です。
実行後はSELECT COUNT(*) FROM 表;で残り行数を確かめ、想定どおり削除されたかを検証します。
-- まず削除対象を確認: 平均価格を下回る商品(実行しても stock は変わらない)
SELECT sku, name, price FROM stock
WHERE price < (SELECT AVG(price) FROM stock);
-- 想定どおりなら DELETE に書き換え、残り行数を確認
DELETE FROM stock
WHERE price < (SELECT AVG(price) FROM stock);
SELECT COUNT(*) AS remaining FROM stock;
理解度チェック
まずは1問ずつ答えてみましょう。
Q2UPDATE stock SET qty = stock.qty + stock_in.add_qty FROM stock_in WHERE stock.sku = stock_in.sku;で、stock_inに対応行が無いstockの行はどう扱われますか。
Q3サブクエリ条件付きのDELETEを本番運用で安全に実行するための手順として最も適切なものはどれですか。