【SQL】UPDATE文でデータを更新する方法|基本構文からサブクエリ・JOIN・一括更新まで完全解説

SQLのUPDATE文は、テーブルに格納されている既存のデータを変更するための命令です。INSERT(追加)・SELECT(取得)・DELETE(削除)と並ぶ、SQL四大操作(CRUD)の一つであり、データベース管理において最も頻繁に使われる操作です。

「特定の顧客の住所を変更したい」「商品の価格を一括で10%値上げしたい」「別テーブルの値を参照して更新したい」など、実務では様々なUPDATE処理が求められます。

この記事では、UPDATE文の基本構文から、サブクエリJOINを使った高度な更新、一括更新時の注意点RDBMS別の違いまで、実務で必要な知識を体系的に解説します。

この記事で学べること

  • UPDATE文の基本構文とSET句・WHERE句の使い方
  • 複数カラムを同時に更新する方法
  • WHERE句の条件パターン(比較・LIKE・IN・BETWEEN・IS NULL)
  • サブクエリを使った更新(別テーブル参照・集計結果で更新)
  • JOINを使った更新(MySQL / PostgreSQL / SQL Server)
  • CASE式で条件分岐しながら更新する方法
  • NULL値の更新と注意点
  • 一括更新のリスクと安全な実行手順
  • UPDATE文のよくあるエラーと対処法
  • RDBMS別のUPDATE構文の違い
スポンサーリンク
  1. サンプルデータの準備
  2. UPDATE文の基本構文
  3. 1つのカラムを更新する
    1. 特定の社員の名前を変更する
    2. 数値を更新する
  4. 複数のカラムを同時に更新する
  5. 計算式を使った更新
    1. 現在の値に加算・減算する
    2. 文字列を結合して更新する
    3. 日付を更新する
  6. WHERE句の条件パターン
    1. 比較演算子(=, !=, <, >, <=, >=)
    2. LIKE(部分一致検索)
    3. IN(複数の値にマッチ)
    4. BETWEEN(範囲指定)
    5. IS NULL / IS NOT NULL
    6. AND / OR(複合条件)
  7. CASE式で条件に応じた値に更新する
    1. 部署ごとに異なる昇給率を適用する
    2. 複数カラムをCASE式で同時に更新する
  8. サブクエリを使ったUPDATE
    1. SET句でサブクエリを使う
    2. WHERE句でサブクエリを使う
    3. EXISTS を使った条件指定
  9. JOINを使ったUPDATE(別テーブルと結合して更新)
    1. MySQL の場合
    2. PostgreSQL の場合
    3. SQL Server の場合
    4. Oracle の場合(MERGE文)
  10. 全レコードを一括で更新する(WHERE句省略)
  11. NULL値の更新と注意点
    1. カラムをNULLに更新する
    2. NULLを含む計算の注意
  12. UPDATE前にSELECTで確認する(安全な実行手順)
    1. 手順1:SELECTで対象行を確認
    2. 手順2:想定通りならUPDATEを実行
    3. 手順3:更新結果をSELECTで確認
  13. トランザクションを使った安全なUPDATE
  14. 実務でよく使うUPDATEパターン集
    1. パターン1:フラグの一括切り替え
    2. パターン2:タイムスタンプの自動更新
    3. パターン3:REPLACE関数で文字列を置換
    4. パターン4:LIMITで更新件数を制限(MySQL限定)
    5. パターン5:連番やランクの更新(ユーザー変数を活用)
  15. UPDATE文のよくあるエラーと対処法
    1. エラー1:WHERE句の付け忘れ(全行更新)
    2. エラー2:データ型の不一致
    3. エラー3:外部キー制約違反
    4. エラー4:サブクエリが複数行を返す
    5. エラー5:ロック競合(デッドロック)
  16. RDBMS別のUPDATE構文の違い
    1. PostgreSQLのRETURNING句
    2. SQL ServerのOUTPUT句
  17. UPDATEのパフォーマンス最適化
    1. インデックスの活用
    2. 大量更新のバッチ処理
  18. UPDATE文のベストプラクティス(まとめ)
  19. UPDATE文 チートシート
  20. よくある質問(FAQ)
  21. まとめ

サンプルデータの準備

この記事では、以下の2つのテーブルを使って解説します。

CREATE TABLE + INSERT文(クリックで展開)
-- 社員テーブル
CREATE TABLE employees (
    id         INT PRIMARY KEY,
    name       VARCHAR(50),
    department VARCHAR(30),
    salary     INT,
    email      VARCHAR(100),
    hire_date  DATE,
    status     VARCHAR(10) DEFAULT 'active'
);

INSERT INTO employees VALUES
(1,  '田中太郎',   '営業部',   350000, 'tanaka@example.com',  '2020-04-01', 'active'),
(2,  '佐藤花子',   '開発部',   420000, 'sato@example.com',   '2019-07-15', 'active'),
(3,  '鈴木一郎',   '営業部',   300000, 'suzuki@example.com', '2021-01-10', 'active'),
(4,  '高橋美咲',   '人事部',   380000, 'takahashi@example.com', '2018-10-01', 'active'),
(5,  '伊藤健太',   '開発部',   450000, 'ito@example.com',    '2017-04-01', 'active'),
(6,  '渡辺直美',   '総務部',   320000, 'watanabe@example.com', '2022-04-01', 'active'),
(7,  '山本翔太',   '営業部',   280000, NULL,                       '2023-04-01', 'active'),
(8,  '中村悠太',   '開発部',   400000, 'nakamura@example.com', '2020-10-01', 'inactive');

-- 部署マスタテーブル
CREATE TABLE departments (
    dept_name    VARCHAR(30) PRIMARY KEY,
    base_salary  INT,
    bonus_rate   DECIMAL(3,2)
);

INSERT INTO departments VALUES
('営業部',   300000, 1.20),
('開発部',   400000, 1.15),
('人事部',   350000, 1.10),
('総務部',   320000, 1.10);

employees テーブル(社員テーブル)

id name department salary email hire_date status
1 田中太郎 営業部 350000 tanaka@example.com 2020-04-01 active
2 佐藤花子 開発部 420000 sato@example.com 2019-07-15 active
3 鈴木一郎 営業部 300000 suzuki@example.com 2021-01-10 active
4 高橋美咲 人事部 380000 takahashi@example.com 2018-10-01 active
5 伊藤健太 開発部 450000 ito@example.com 2017-04-01 active
6 渡辺直美 総務部 320000 watanabe@example.com 2022-04-01 active
7 山本翔太 営業部 280000 NULL 2023-04-01 active
8 中村悠太 開発部 400000 nakamura@example.com 2020-10-01 inactive

departments テーブル(部署マスタ)

dept_name base_salary bonus_rate
営業部 300000 1.20
開発部 400000 1.15
人事部 350000 1.10
総務部 320000 1.10

UPDATE文の基本構文

UPDATE文の基本的な構文は以下のとおりです。

UPDATE文の基本構文
UPDATE テーブル名
SET カラム名1 = 値1,
    カラム名2 = 値2,
    ...
WHERE 条件式;
キーワード 役割
UPDATE 更新対象のテーブルを指定
SET 変更するカラムと新しい値を指定(カンマ区切りで複数指定可)
WHERE 更新対象の行を絞り込む条件(省略すると全行が対象)

注意:WHERE句を省略すると、テーブル内の全レコードが更新されます。意図しない一括更新を防ぐため、UPDATE文にはWHERE句を付けることを習慣にしましょう。

1つのカラムを更新する

最もシンプルなUPDATE文です。WHERE句で対象行を特定し、SET句で値を変更します。

特定の社員の名前を変更する

SQL
UPDATE employees
SET name = '田中次郎'
WHERE id = 1;

id が 1 の社員(田中太郎)の名前を「田中次郎」に変更します。WHERE句で id = 1 と指定しているため、1行だけが更新されます。

実行結果

Query OK, 1 row affected

数値を更新する

SQL
UPDATE employees
SET salary = 400000
WHERE id = 3;

数値カラムの場合、値をクォーテーションで囲む必要はありません。id が 3 の鈴木一郎の給料を 300,000 から 400,000 に変更します。

ポイント:文字列はシングルクォーテーションで囲みますが、数値はクォーテーション不要です。日付型は RDBMS によりますが、一般的にはシングルクォーテーションで囲みます。

複数のカラムを同時に更新する

SET句でカンマ区切りにすることで、1つのUPDATE文で複数カラムを同時に更新できます。

部署と給料を同時に変更
UPDATE employees
SET department = '開発部',
    salary     = 380000,
    email      = 'suzuki.dev@example.com'
WHERE id = 3;

鈴木一郎の所属部署を「営業部」から「開発部」に異動させ、同時に給料とメールアドレスも更新しています。このように関連する変更は1つのUPDATE文にまとめるのが効率的です。

注意:SET句の最後のカラムの後にカンマを付けるとエラーになります。SET a = 1, b = 2,のように末尾にカンマが残らないよう注意しましょう。

計算式を使った更新

SET句では、固定値だけでなく計算式を使って値を更新できます。現在の値を基に加減算したり、関数を使ったりすることが可能です。

現在の値に加算・減算する

給料を10%アップ
UPDATE employees
SET salary = salary * 1.1
WHERE id = 1;

salary = salary * 1.1 で現在の給料に 1.1 を掛けて 10% アップしています。SET句の右辺では、更新前の値を参照できるのがポイントです。

文字列を結合して更新する

MySQL / PostgreSQL
-- MySQL
UPDATE employees
SET name = CONCAT(name, '(退職)')
WHERE status = 'inactive';

-- PostgreSQL
UPDATE employees
SET name = name || '(退職)'
WHERE status = 'inactive';

status が「inactive」の社員の名前の末尾に「(退職)」を追加しています。MySQLでは CONCAT() 関数、PostgreSQLでは || 演算子で文字列を結合します。

日付を更新する

SQL
-- 固定の日付を設定
UPDATE employees
SET hire_date = '2024-04-01'
WHERE id = 7;

-- 現在日時を設定(MySQL)
UPDATE employees
SET hire_date = CURDATE()
WHERE id = 7;

日付型のカラムには 'YYYY-MM-DD' 形式の文字列か、CURDATE()(MySQL)や CURRENT_DATE(標準SQL)などの関数を使います。

パターン SET句の書き方 説明
加算 salary = salary + 10000 10,000円を加算
減算 salary = salary - 5000 5,000円を減算
乗算(割合) salary = salary * 1.1 10%アップ
除算 salary = salary / 2 半額にする
文字列結合 name = CONCAT(name, '様') 末尾に文字列追加
現在日時 updated_at = NOW() 現在のタイムスタンプ
NULLをセット email = NULL 値をNULLにクリア

WHERE句の条件パターン

UPDATE文でどの行を更新するかを決めるのがWHERE句です。SELECT文と同じ条件式が使えます。ここでは実務でよく使うパターンを紹介します。

比較演算子(=, !=, <, >, <=, >=)

比較演算子の例
-- 給料が300,000未満の社員を310,000に更新
UPDATE employees
SET salary = 310000
WHERE salary < 300000;

LIKE(部分一致検索)

LIKE句の例
-- メールアドレスのドメインを変更
UPDATE employees
SET email = REPLACE(email, '@example.com', '@newdomain.com')
WHERE email LIKE '%@example.com';

% は0文字以上の任意の文字列にマッチするワイルドカードです。_ は1文字にマッチします。

IN(複数の値にマッチ)

IN句の例
-- 営業部と総務部の社員を一律5%昇給
UPDATE employees
SET salary = salary * 1.05
WHERE department IN ('営業部', '総務部');

IN句を使うと、複数の値のいずれかにマッチする行を対象にできます。OR を複数書くよりも簡潔です。

BETWEEN(範囲指定)

BETWEEN句の例
-- 入社日が2020年の社員のステータスを更新
UPDATE employees
SET status = 'veteran'
WHERE hire_date BETWEEN '2020-01-01' AND '2020-12-31';

BETWEEN は両端を含む範囲指定です。hire_date >= '2020-01-01' AND hire_date <= '2020-12-31' と同じ意味になります。

IS NULL / IS NOT NULL

NULLの判定
-- メールアドレスが未登録の社員にデフォルト値を設定
UPDATE employees
SET email = 'unknown@example.com'
WHERE email IS NULL;

注意:NULLの比較には = NULL ではなく、必ず IS NULL を使います。WHERE email = NULL は常に偽となり、1行も更新されません。これはSQLの三値論理によるもので、NULL との比較は常に UNKNOWN(不明)となるためです。

AND / OR(複合条件)

複合条件の例
-- 開発部で給料が400,000以上の社員をリーダーに昇進
UPDATE employees
SET status = 'leader'
WHERE department = '開発部'
  AND salary >= 400000
  AND status = 'active';

AND で条件をすべて満たす行、OR でいずれかの条件を満たす行が対象になります。AND と OR を混在させる場合は、括弧 () で優先順位を明示しましょう。

演算子 使い方 説明
= WHERE id = 1 等しい
!= / <> WHERE status != 'active' 等しくない
<, >, <=, >= WHERE salary >= 300000 比較
LIKE WHERE name LIKE '田中%' パターンマッチ
IN WHERE id IN (1, 2, 3) リスト内の値
BETWEEN WHERE salary BETWEEN 300000 AND 400000 範囲(両端含む)
IS NULL WHERE email IS NULL NULLである
IS NOT NULL WHERE email IS NOT NULL NULLでない

CASE式で条件に応じた値に更新する

SET句の中でCASE式を使うと、条件に応じて異なる値を設定できます。複数のUPDATE文を1つにまとめられるため、効率的です。

部署ごとに異なる昇給率を適用する

CASE式で条件分岐
UPDATE employees
SET salary = CASE
    WHEN department = '営業部' THEN salary * 1.10
    WHEN department = '開発部' THEN salary * 1.15
    WHEN department = '人事部' THEN salary * 1.05
    ELSE salary  -- 条件に合わない場合は変更なし
END
WHERE status = 'active';

営業部は10%、開発部は15%、人事部は5%とそれぞれ異なる昇給率を1つのUPDATE文で適用しています。ELSE salary を書くことで、条件に合わない部署(総務部など)の給料は変更されません。

ポイント:CASE式で ELSE を省略すると、条件に合わない行はNULLに更新されてしまいます。意図しないデータ消失を防ぐため、ELSE句は必ず書くことをおすすめします。

複数カラムをCASE式で同時に更新する

複数カラムのCASE式
UPDATE employees
SET
    salary = CASE
        WHEN id = 1 THEN 360000
        WHEN id = 2 THEN 430000
        WHEN id = 3 THEN 310000
        ELSE salary
    END,
    department = CASE
        WHEN id = 3 THEN '開発部'
        ELSE department
    END
WHERE id IN (1, 2, 3);

id ごとに給料を個別設定し、さらに id=3 の部署も同時に変更しています。このパターンは、バッチ処理などで行ごとに異なる値を一括更新したい場合に便利です。

サブクエリを使ったUPDATE

SET句やWHERE句にサブクエリ(副問合せ)を書くことで、別のテーブルや集計結果を参照して更新できます。

SET句でサブクエリを使う

別テーブルの値を参照して、更新先のカラムに代入するパターンです。

部署マスタの基本給で更新
UPDATE employees e
SET salary = (
    SELECT d.base_salary
    FROM departments d
    WHERE d.dept_name = e.department
)
WHERE status = 'active';

departments テーブルから部署名が一致する行の base_salary を取得し、employees の salary にセットしています。サブクエリが1行1列の値を返す必要がある点に注意してください。

注意:SET句のサブクエリが複数行を返すとエラーになります。サブクエリが必ず1行だけ返すよう、結合条件を正しく指定しましょう。マッチしない場合はNULLがセットされます。

WHERE句でサブクエリを使う

WHERE句のサブクエリは、更新対象の行を絞り込むために使います。

平均以下の給料を底上げ
UPDATE employees
SET salary = salary * 1.05
WHERE salary < (
    SELECT AVG(salary)
    FROM employees
);

社員全体の平均給料を算出し、それ未満の社員を5%昇給させています。

注意(MySQL):MySQLでは、UPDATE対象のテーブル自身をサブクエリで直接参照するとエラーになります。その場合は、サブクエリをさらにラップして派生テーブルにします。

MySQL向け:派生テーブルでラップ
UPDATE employees
SET salary = salary * 1.05
WHERE salary < (
    SELECT avg_sal FROM (
        SELECT AVG(salary) AS avg_sal FROM employees
    ) tmp
);

EXISTS を使った条件指定

EXISTS句の例
-- 部署マスタに存在する部署の社員だけ更新
UPDATE employees e
SET salary = salary * 1.03
WHERE EXISTS (
    SELECT 1
    FROM departments d
    WHERE d.dept_name = e.department
);

EXISTS は「サブクエリが1行以上返すかどうか」を判定します。IN と似ていますが、大量データの場合はEXISTSの方がパフォーマンスが良いことが多いです。

JOINを使ったUPDATE(別テーブルと結合して更新)

サブクエリよりも直感的で高速な方法として、JOINを使ったUPDATEがあります。ただし、構文はRDBMSごとに異なるため注意が必要です。

MySQL の場合

MySQLでは UPDATE ... JOIN ... SET ... の構文を使います。

MySQL – JOINを使ったUPDATE
UPDATE employees e
INNER JOIN departments d
    ON e.department = d.dept_name
SET e.salary = d.base_salary * d.bonus_rate;

employees テーブルと departments テーブルを department で結合し、基本給 x ボーナス率 で各社員の給料を更新しています。MySQLの JOIN UPDATE は SET句がJOINの後に来るのが特徴です。

PostgreSQL の場合

PostgreSQLでは UPDATE ... SET ... FROM ... WHERE ... の構文を使います。

PostgreSQL – FROM句を使ったUPDATE
UPDATE employees e
SET salary = d.base_salary * d.bonus_rate
FROM departments d
WHERE e.department = d.dept_name;

PostgreSQLでは FROM 句で結合先テーブルを指定し、WHERE 句で結合条件を書きます。MySQLとは構文が大きく異なる点に注意してください。

SQL Server の場合

SQL Server – FROM句 + JOINを使ったUPDATE
UPDATE e
SET e.salary = d.base_salary * d.bonus_rate
FROM employees e
INNER JOIN departments d
    ON e.department = d.dept_name;

SQL Serverでは UPDATE エイリアス SET ... FROM テーブル JOIN ... の構文です。UPDATE句にはテーブルのエイリアスを書きます。

Oracle の場合(MERGE文)

OracleにはJOIN UPDATEの直接的な構文がないため、MERGE文を使うのが一般的です。

Oracle – MERGE文
MERGE INTO employees e
USING departments d
    ON (e.department = d.dept_name)
WHEN MATCHED THEN
    UPDATE SET e.salary = d.base_salary * d.bonus_rate;
RDBMS JOIN UPDATEの構文
MySQL UPDATE t1 JOIN t2 ON ... SET t1.col = t2.col
PostgreSQL UPDATE t1 SET col = t2.col FROM t2 WHERE ...
SQL Server UPDATE t1 SET t1.col = t2.col FROM t1 JOIN t2 ON ...
Oracle MERGE INTO t1 USING t2 ON (...) WHEN MATCHED THEN UPDATE SET ...

全レコードを一括で更新する(WHERE句省略)

WHERE句を省略すると、テーブル内のすべての行が更新されます。意図的に全行更新したい場合に使いますが、細心の注意が必要です。

全行更新の例
-- 全社員のステータスをactiveにリセット
UPDATE employees
SET status = 'active';

-- 全社員の給料を一律5%カット
UPDATE employees
SET salary = salary * 0.95;

注意:WHERE句を付け忘れて全行更新してしまう事故は、実務で最も多いミスの一つです。UPDATE文を実行する前に、必ず同じWHERE条件でSELECTを実行して対象行を確認する習慣をつけましょう。

NULL値の更新と注意点

NULLは「値が存在しない」ことを表す特殊な値です。UPDATE文でのNULLの扱いには、いくつかの注意点があります。

カラムをNULLに更新する

NULLをセット
-- メールアドレスをクリア
UPDATE employees
SET email = NULL
WHERE id = 8;

NULL はクォーテーションで囲みません。'NULL'(文字列の “NULL”)とは別物なので注意してください。

NULLを含む計算の注意

NULLを含む計算
-- NULLに加算するとNULLになる!
UPDATE employees
SET salary = salary + 10000
WHERE id = 9;  -- salaryがNULLなら結果もNULL

-- COALESCE()でNULLを安全に処理
UPDATE employees
SET salary = COALESCE(salary, 0) + 10000
WHERE id = 9;  -- NULLなら0として計算

NULL + 10000 の結果は NULL です(10000ではありません)。COALESCE() 関数を使って NULL を 0 に変換してから計算すると安全です。

結果 理由
NULL + 10000 NULL NULLとの演算結果はNULL
NULL * 1.1 NULL NULLとの演算結果はNULL
CONCAT(NULL, 'abc') NULL(MySQL)/ ‘abc’(PostgreSQL) RDBMSにより異なる
NULL = NULL UNKNOWN(偽扱い) 三値論理
NULL IS NULL TRUE IS NULLで正しく判定

UPDATE前にSELECTで確認する(安全な実行手順)

実務では、UPDATE文を実行する前に同じWHERE条件でSELECTを実行し、更新対象の行を確認するのが鉄則です。

手順1:SELECTで対象行を確認

Step 1: SELECTで確認
-- まずSELECTで対象を確認
SELECT id, name, salary, department
FROM employees
WHERE department = '営業部'
  AND status = 'active';

実行結果

+----+----------+--------+------------+
| id | name     | salary | department |
+----+----------+--------+------------+
|  1 | 田中太郎 | 350000 | 営業部     |
|  3 | 鈴木一郎 | 300000 | 営業部     |
|  7 | 山本翔太 | 280000 | 営業部     |
+----+----------+--------+------------+
3 rows in set

手順2:想定通りならUPDATEを実行

Step 2: UPDATEを実行
UPDATE employees
SET salary = salary * 1.10
WHERE department = '営業部'
  AND status = 'active';

手順3:更新結果をSELECTで確認

Step 3: 結果を確認
SELECT id, name, salary, department
FROM employees
WHERE department = '営業部';

実行結果

+----+----------+--------+------------+
| id | name     | salary | department |
+----+----------+--------+------------+
|  1 | 田中太郎 | 385000 | 営業部     |
|  3 | 鈴木一郎 | 330000 | 営業部     |
|  7 | 山本翔太 | 308000 | 営業部     |
+----+----------+--------+------------+
3 rows in set

ポイント:本番環境では、UPDATE前にトランザクションを開始しておくと、問題があった場合にROLLBACKで元に戻せます。

トランザクションを使った安全なUPDATE

トランザクションを使うと、UPDATE文の実行結果を確認してから確定(COMMIT)するか、取り消し(ROLLBACK)するかを選べます。本番環境でのUPDATE作業には必須のテクニックです。

トランザクションでUPDATEを安全に実行
-- トランザクション開始
BEGIN;  -- MySQL: START TRANSACTION; でも可

-- UPDATEを実行
UPDATE employees
SET salary = salary * 1.10
WHERE department = '営業部';

-- 結果を確認
SELECT * FROM employees WHERE department = '営業部';

-- 問題なければ確定
COMMIT;

-- 問題があれば取り消し
-- ROLLBACK;
コマンド 動作
BEGIN / START TRANSACTION トランザクションを開始する
COMMIT 変更を確定する(元に戻せなくなる)
ROLLBACK 変更をすべて取り消して元に戻す

注意:MySQLのデフォルト設定では autocommit = ON になっており、各SQL文が自動的にCOMMITされます。トランザクションを使うには、明示的に BEGIN または START TRANSACTION を実行してから UPDATE を行ってください。

実務でよく使うUPDATEパターン集

ここでは、実際の業務で頻繁に使われるUPDATEのパターンを紹介します。

パターン1:フラグの一括切り替え

論理削除の実装例
-- 退職した社員を論理削除(物理削除せずフラグで管理)
UPDATE employees
SET status = 'inactive'
WHERE id = 8;

-- 全フラグをリセット
UPDATE employees
SET status = 'active'
WHERE status = 'inactive';

物理的にDELETEするのではなく、statusカラムで「active / inactive」を切り替える論理削除は、実務で非常によく使われるパターンです。

パターン2:タイムスタンプの自動更新

更新日時の記録
UPDATE employees
SET
    salary     = 450000,
    updated_at = NOW()
WHERE id = 5;

データを変更する際に updated_at = NOW() で更新日時も同時に記録しておくと、「いつ変更されたか」をトレースできます。

パターン3:REPLACE関数で文字列を置換

文字列の一括置換
-- メールアドレスのドメインを一括変更
UPDATE employees
SET email = REPLACE(email, '@example.com', '@newcompany.com')
WHERE email LIKE '%@example.com';

REPLACE関数は文字列内の特定の部分を別の文字列に置換します。会社の合併やドメイン変更時に便利です。

パターン4:LIMITで更新件数を制限(MySQL限定)

MySQL – LIMIT付きUPDATE
-- 最初の3件だけ更新
UPDATE employees
SET salary = salary * 1.05
WHERE department = '営業部'
ORDER BY hire_date ASC
LIMIT 3;

MySQLでは ORDER BYLIMIT を組み合わせて、更新する行数を制限できます。大量データを少しずつ更新したい場合に使います。PostgreSQLやOracleには、この構文はありません。

パターン5:連番やランクの更新(ユーザー変数を活用)

MySQL – ユーザー変数で連番を付与
SET @rank := 0;

UPDATE employees
SET id = (@rank := @rank + 1)
ORDER BY hire_date ASC;

ユーザー変数 @rank を使って、入社日順にIDを振り直しています。これはMySQL固有の機能であり、他のRDBMSでは異なるアプローチが必要です。

UPDATE文のよくあるエラーと対処法

UPDATE文で遭遇しやすいエラーとその対処法をまとめます。

エラー1:WHERE句の付け忘れ(全行更新)

項目 内容
状況 WHERE句を書き忘れて全行が更新されてしまった
原因 UPDATE employees SET status = 'inactive'; のようにWHERE句がない
対処法 トランザクション内で実行していればROLLBACK。そうでなければバックアップから復旧

予防策:MySQLの場合、--safe-updates オプション(SET sql_safe_updates = 1;)を有効にすると、WHERE句なしのUPDATE/DELETEが拒否されます。

セーフモードの有効化
-- セーフモードを有効化
SET sql_safe_updates = 1;

-- これはエラーになる(WHERE句なし)
UPDATE employees SET status = 'active';
-- ERROR 1175: You are using safe update mode...

エラー2:データ型の不一致

項目 内容
エラー Incorrect integer value / Data truncated
原因 INT型のカラムに文字列を入れようとした、VARCHAR型の桁数を超えた等
対処法 カラムの型と桁数を確認し、適切な値を指定する
エラー例と修正
-- NG: INT型のカラムに文字列
UPDATE employees SET salary = '高い' WHERE id = 1;

-- OK: INT型には数値を指定
UPDATE employees SET salary = 500000 WHERE id = 1;

エラー3:外部キー制約違反

項目 内容
エラー Cannot add or update a child row: a foreign key constraint fails
原因 外部キーで参照されている値を、参照先に存在しない値に変更しようとした
対処法 参照先テーブルに対象の値が存在するか確認してから更新する

エラー4:サブクエリが複数行を返す

項目 内容
エラー Subquery returns more than 1 row
原因 SET句のサブクエリが複数行を返している
対処法 結合条件を見直すか、LIMIT 1やMAX()/MIN()で1行に絞る
修正例
-- NG: サブクエリが複数行を返す可能性
UPDATE employees
SET salary = (SELECT base_salary FROM departments);

-- OK: 結合条件で1行に絞る
UPDATE employees e
SET salary = (
    SELECT d.base_salary
    FROM departments d
    WHERE d.dept_name = e.department
);

エラー5:ロック競合(デッドロック)

項目 内容
エラー Deadlock found when trying to get lock / Lock wait timeout exceeded
原因 複数のトランザクションが互いにロックを待っている状態
対処法 更新順序を統一する、トランザクションを短くする、バッチサイズを小さくする

RDBMS別のUPDATE構文の違い

UPDATE文の基本構文はどのRDBMSでも共通ですが、拡張機能には違いがあります。主要なRDBMSの違いをまとめます。

機能 MySQL PostgreSQL SQL Server Oracle
基本 UPDATE SET WHERE 対応 対応 対応 対応
JOIN UPDATE UPDATE JOIN SET UPDATE SET FROM WHERE UPDATE FROM JOIN MERGE INTO
ORDER BY + LIMIT 対応 非対応 TOP N ROWNUM
RETURNING句 非対応 対応 OUTPUT句 RETURNING INTO
自テーブル参照サブクエリ 派生テーブル必須 そのまま可 そのまま可 そのまま可
文字列結合 CONCAT() || + ||

PostgreSQLのRETURNING句

PostgreSQLでは、UPDATE文に RETURNING を付けると、更新後の値をSELECT結果のように返せます。

PostgreSQL – RETURNING句
UPDATE employees
SET salary = salary * 1.10
WHERE department = '営業部'
RETURNING id, name, salary;

実行結果

 id |   name   | salary
----+----------+--------
  1 | 田中太郎 | 385000
  3 | 鈴木一郎 | 330000
  7 | 山本翔太 | 308000
(3 rows)

RETURNING句を使うと、UPDATE完了と同時に更新後の値を確認できるため、手順が1ステップ減ります。

SQL ServerのOUTPUT句

SQL Server – OUTPUT句
UPDATE employees
SET salary = salary * 1.10
OUTPUT
    inserted.id,
    inserted.name,
    deleted.salary  AS old_salary,
    inserted.salary AS new_salary
WHERE department = '営業部';

SQL ServerのOUTPUT句では、inserted(更新後)と deleted(更新前)の両方を参照できるため、変更前後の値を同時に確認できます。

UPDATEのパフォーマンス最適化

大量のデータを更新する場合、パフォーマンスに注意が必要です。

インデックスの活用

WHERE句で使用するカラムにインデックスが張られていると、更新対象の行を高速に見つけることができます。

インデックスの確認と作成
-- WHERE句のカラムにインデックスがあるか確認
SHOW INDEX FROM employees;

-- インデックスを作成
CREATE INDEX idx_department ON employees(department);

-- 実行計画で確認
EXPLAIN UPDATE employees
SET salary = salary * 1.1
WHERE department = '営業部';

大量更新のバッチ処理

100万件以上のデータを一度に更新すると、ロック時間が長くなり他のクエリに影響します。バッチに分割して更新するのがベストプラクティスです。

MySQL – バッチ処理で大量更新
-- 1,000件ずつバッチ更新
UPDATE employees
SET salary = salary * 1.05
WHERE status = 'active'
  AND id BETWEEN 1 AND 1000;

UPDATE employees
SET salary = salary * 1.05
WHERE status = 'active'
  AND id BETWEEN 1001 AND 2000;
-- ...以下繰り返し
方法 メリット デメリット
一括UPDATE シンプル、トランザクション1回 ロック時間が長い、メモリ大量使用
バッチ更新 ロック時間が短い、他のクエリに影響しにくい 処理が複雑、途中でエラーが起きた場合の考慮が必要
pt-online-schema-change等 無停止で大量更新可能 ツールの導入が必要

UPDATE文のベストプラクティス(まとめ)

最後に、UPDATE文を安全かつ効率的に使うためのベストプラクティスをまとめます。

UPDATE文 10のルール

  • WHERE句を必ず付ける — 全行更新を防ぐ
  • 事前にSELECTで確認する — 同じWHERE条件で対象行を確認
  • トランザクションを使う — 問題があればROLLBACKで元に戻せる
  • バックアップを取る — 大量更新前には必ずバックアップ
  • CASE式のELSEを忘れない — 意図しないNULL更新を防ぐ
  • NULLの比較には IS NULL を使う — = NULL は常に偽になる
  • NULL計算にはCOALESCEを使う — NULL + 数値 = NULL を防ぐ
  • 大量更新はバッチに分割する — ロック時間を短くする
  • 本番はセーフモードを検討する — sql_safe_updates = 1
  • RDBMS別の構文差に注意する — 特にJOIN UPDATEは構文が異なる

UPDATE文 チートシート

この記事で紹介したUPDATE文のパターンを一覧にまとめます。コピー&ペーストしてお使いください。

UPDATE文チートシート
-- ■ 基本の更新
UPDATE テーブル SET カラム = 値 WHERE 条件;

-- ■ 複数カラム更新
UPDATE テーブル SET col1 = val1, col2 = val2 WHERE 条件;

-- ■ 計算式で更新
UPDATE テーブル SET salary = salary * 1.1 WHERE 条件;

-- ■ CASE式で条件分岐
UPDATE テーブル SET col = CASE WHEN 条件1 THEN 値1 ELSE col END WHERE ...;

-- ■ サブクエリで更新(SET句)
UPDATE t1 SET col = (SELECT val FROM t2 WHERE t2.id = t1.id);

-- ■ JOIN更新(MySQL)
UPDATE t1 JOIN t2 ON t1.id = t2.id SET t1.col = t2.col;

-- ■ JOIN更新(PostgreSQL)
UPDATE t1 SET col = t2.col FROM t2 WHERE t1.id = t2.id;

-- ■ NULLをセット
UPDATE テーブル SET col = NULL WHERE 条件;

-- ■ NULLを安全に更新
UPDATE テーブル SET col = COALESCE(col, 0) + 100 WHERE 条件;

-- ■ トランザクション
BEGIN;
UPDATE テーブル SET col = val WHERE 条件;
-- 確認後 → COMMIT; または ROLLBACK;

よくある質問(FAQ)

Q. WHERE句を書き忘れた場合、全レコードが更新されてしまいます。防ぐ方法はありますか?
A. まずSELECTで更新対象を確認してからUPDATEを実行する習慣が重要です。また、DBによってはSET SQL_SAFE_UPDATES=1(MySQL)でインデックスなしのUPDATEを防げます。本番環境ではトランザクション(BEGIN/ROLLBACK)の中でUPDATEを実行し、件数を確認してからCOMMITするのが安全です。
Q. 他のテーブルの値を参照してUPDATEする(JOIN UPDATE)にはどうすればよいですか?
A. MySQLではUPDATE table1 t1 JOIN table2 t2 ON t1.id=t2.id SET t1.col=t2.col WHERE ...、SQL ServerとPostgreSQLではUPDATE t1 SET col=t2.col FROM table2 t2 WHERE t1.id=t2.id、OracleではUPDATE (SELECT ... FROM t1 JOIN t2 ...) SET col=...またはサブクエリを使います。
Q. 大量のレコード(100万件以上)を一括UPDATEする場合の注意点は?
A. 大量UPDATEはロック・ログ膨張・パフォーマンス低下の原因になります。バッチサイズ(例:1万件)に分割してコミットすること、UPDATEするカラムにインデックスが不要でないか確認すること、メンテナンス時間帯に実行することを推奨します。

まとめ

この記事では、SQLのUPDATE文について基本構文から応用テクニックまで解説しました。

セクション ポイント
基本構文 UPDATE テーブル SET カラム = 値 WHERE 条件
複数カラム SET句でカンマ区切り、末尾カンマに注意
計算式 SET salary = salary * 1.1 で現在値ベースの更新可能
WHERE句 =, LIKE, IN, BETWEEN, IS NULL, AND/ORの組み合わせ
CASE式 条件ごとに異なる値を設定、ELSEで元の値を保持
サブクエリ SET句は1行1列を返す必要あり、MySQLは派生テーブルが必要
JOIN更新 RDBMS別に構文が異なるので注意
NULL IS NULLで判定、COALESCE()で安全に計算
安全な運用 SELECT確認 → BEGIN → UPDATE → COMMIT/ROLLBACK
パフォーマンス インデックス活用、大量更新はバッチ分割

UPDATE文は非常に強力な反面、WHERE句の付け忘れデータ型の不一致で意図しない結果になることがあります。「事前のSELECT確認」「トランザクションの活用」「バックアップの取得」の3つを習慣にすることで、安全にデータ更新を行えるようになります。

ぜひ、この記事のサンプルを実際に手を動かして試してみてください。実践を通じてUPDATE文の挙動を体感することが、スキル向上への近道です。