コマンド道場

UPDATE の使い方

WHERE が「どの行を書き換えるか」を決める

shain社員

namebusho_idsalary
中村2530000
佐藤2720000
井上7505000
伊藤3530000

WHERE で対象を選ぶ書き換わる行

namebusho_idsalary
中村2530000
佐藤2720000

WHERE busho_id = 2

  • 取り消し線= 条件に合わないので結果に出てこない行
  • 社員 18 行 → 書き換わる行 414 行が消えます)

取り消し線の行は対象外です。WHERE を書かないと、この線がすべて消えて全行が対象になります。

UPDATEすでにある行の値を書き換える文です。危険なのは WHERE の書き忘れで、1 行のつもりが全行変わります。しかも元に戻せません。実行する前に、同じ条件で SELECT して対象を確かめる癖をつけてください。

主要 7 製品で通用 そのまま使える: PostgreSQL / MySQL / MariaDB / SQLite / SQL Server / Oracle Database / IBM Db2

製品ごとの対応表(実測)を見る

このページの実行結果と図は、すべて本物のデータベースに同じ SQL を流して得たものです。手で書いた「たぶんこうなる」ではありません。

01基本形は UPDATE … SET … WHERE

結論: `UPDATE 表 SET 列 = 値 WHERE 条件` の 3 つで書きます。

SET に書くのは「新しい値」、WHERE に書くのは「どの行を変えるか」です。WHERE を省略すると表の全行が対象になります。

このページの例では RETURNING を付けています。更新した行をその場で返す書き方で、何がどう変わったかを 1 文で確かめられます。

⚠️ RETURNING は PostgreSQL・SQLite・MariaDB で使えます。SQL Server は OUTPUT、MySQL には相当するものがありません。確認のために `SELECT` を別に流すのが、どの製品でも通るやり方です。

1 行ずつ、何をしているか
  1. UPDATE shainどの表を変えるか
  2. SET salary = salary + 10000新しい値(式も書ける)
  3. WHERE busho_id = 2どの行を変えるか
  4. RETURNING name, salary変えた行を返す(確認用)

WHERE を消すと全行が対象になります。書き忘れがいちばん多い事故です。

まず対象を確かめる
SELECT shain_id, name, salary FROM shain WHERE busho_id = 2 ORDER BY shain_id;
shain_idnamesalary
2佐藤720000
3田中520000
4中村530000
5小林445050

4

UPDATE を書く前に、同じ WHERE で SELECT します。件数と中身がここで分かります。

開発部の給与を 1 万円上げ、前後を並べて返す
UPDATE shain SET salary = salary + 10000 WHERE busho_id = 2 RETURNING shain_id, name, salary - 10000 AS mae, salary AS ato;
shain_idnamemaeato
2佐藤720000730000
3田中520000530000
4中村530000540000
5小林445050455050

4

4 行だけが返っています。RETURNING には式も書けるので、変更前の値を計算して並べられます。

02⚠️ WHERE を書き忘れると全行が変わる

結論: `WHERE` の無い `UPDATE` は表の全行を書き換えます。

エラーにも警告にもなりません。実行した瞬間に、関係のない行まで値が変わります。

この演習環境は「初期状態に戻す」で元に戻せますが、本番のデータベースでは戻せませんUPDATE を書くときは、先に WHERE を書いてから SET を書く、という順番にすると忘れにくくなります。

実務では次の 2 つが効きます。

  • トランザクションの中で試すBEGIN; で始めて UPDATE を流し、件数を確かめてから COMMIT;(違えば ROLLBACK;
  • 更新された行数を見る。想定と違う件数が返ったら、そこで止まって条件を見直す
⚠️ WHERE が無い(全員が対象になる)
UPDATE shain SET salary = 999999 RETURNING shain_id, name, salary;
shain_idnamesalary
1鈴木999999
2佐藤999999
3田中999999
4中村999999
5小林999999
6加藤999999

18 … うち先頭 6 行を表示

18 行が返りました。返ってきた行数がそのまま「変えてしまった行数」です。4 行のつもりが 18 行なら、そこで気づけます。

03SET に CASE を書いて出し分ける

結論: `SET` の右辺には `CASE` を書けます。行ごとに違う値を入れられます。

⚠️ このとき `ELSE` を書かないと、条件に合わない行が `NULL` になります。「変えない」は ELSE 列名 と明示してください。

この表の salary には NOT NULL が付いているので、ELSE を省くとエラーで止まります。制約が事故を防いでくれた形です。制約が無い列なら、そのまま値が消えていました。

営業部だけ上げ、他は据え置き
UPDATE shain SET salary = CASE WHEN busho_id = 3 THEN salary + 20000 ELSE salary END RETURNING shain_id, busho_id, salary;
shain_idbusho_idsalary
11980000
22720000
32520000
42530000
52445050
65610000
75468000
83720000

18 … うち先頭 8 行を表示

ELSE salary が「変えない」を表します。全 18 行が返るのは、全行が更新の対象になっているためです(値は変わらなくても対象ではあります)。

⚠️ ELSE を書かないとどうなるか
UPDATE shain SET salary = CASE WHEN busho_id = 3 THEN salary + 20000 END;
ERROR: null value in column "salary" of relation "shain" violates not-null constraint

営業部以外が NULL になろうとして、NOT NULL 制約に阻まれました。制約が無ければ、静かに値が消えていたところです。

04他の表を見て対象を決める

結論: `WHERE` に副問い合わせを書けば、別の表の条件で対象を選べます。

部署番号を直接書くと、番号が変わったときに黙って別の部署を更新してしまいます。名前から引けば、意図が SQL の中に残ります

対象が複数になるときは IN を使います。= は 1 つの値としか比べられないので、副問い合わせが 2 行以上返すとエラーになります。

部署名から対象を引く
UPDATE shain SET salary = salary + 5000 WHERE busho_id = (SELECT busho_id FROM busho WHERE busho_name = '管理') RETURNING shain_id, name, salary;
shain_idnamesalary
14清水655000
15斎藤460000
16山本517000

3

番号ではなく名前で指定しています。組織の番号が変わっても意図は壊れません。返ってきた 3 行が管理部の社員です。

⚠️ 副問い合わせが複数行を返すと = ではエラー
UPDATE shain SET salary = salary + 5000 WHERE busho_id = (SELECT busho_id FROM busho WHERE parent_busho_id = 1);
ERROR: more than one row returned by a subquery used as an expression

最上位部署の直下は複数あるため、= では受け取れません。次の例のように IN を使います。

対象が複数なら IN を使う
UPDATE shain SET salary = salary + 5000 WHERE busho_id IN (SELECT busho_id FROM busho WHERE parent_busho_id = 1) RETURNING shain_id, busho_id, salary;
shain_idbusho_idsalary
22725000
32525000
42535000
52450050
83705000
93485000
103535000
113426000
144655000
154460000
164517000

11

IN なら複数行を受け取れます。返ってきた行の busho_id を見れば、対象の部署が意図どおりか確かめられます。

05更新できないこともある

結論: 制約に反する値には更新できません。

この表の salary には「0 より大きい」という検査制約が付いています。それに反する値を入れようとすると、更新は行われずにエラーになります。

エラーで止まるのは安全側の動きです。何も起きなかったのではなく、制約が守ってくれたと読んでください。1 行でも制約に反すると、その文全体が取り消されます。一部だけ更新されて中途半端に終わることはありません

⚠️ 制約に反する更新
UPDATE shain SET salary = 0 WHERE shain_id = 1;
ERROR: new row for relation "shain" violates check constraint "shain_salary_check"

salary > 0 という検査制約があるため拒否されます。表の定義が誤ったデータを防いでいます。

⚠️ 1 行でも違反すると、その文全体が取り消される
UPDATE shain SET salary = CASE WHEN shain_id = 1 THEN 0 ELSE salary + 1000 END;
ERROR: new row for relation "shain" violates check constraint "shain_salary_check"

他の 17 人は正しい値だったのに、1 人が制約に反したため何も更新されていません。文の単位で全部通るか全部やめるかが決まります。

自分で打ってみる

このページの例と同じデータが入った本物のデータベースを、この場(あなたのブラウザの中)で起動できます。打った SQL がサーバーへ送られることはありません。

押すと初回だけ約 9MB を読み込みます(2 回目以降はキャッシュされます)。

解いてみる

読んだだけでは書けるようになりません。実行結果で採点します。

関連するトピック

  • DELETE(練習問題)行を消す DELETE も、WHERE の書き忘れが同じ事故になります。
  • WHERE 句対象を決めるのは WHERE です。書き方はここで扱っています。
  • CASE 式(練習問題)SET の中で値を出し分けるときに使います。ELSE の省略に注意してください。

根拠(一次情報)