「関連する複数のテーブルの情報を使って更新したい」
「他のテーブルの値を参照してデータを更新したい」
「JOINを使いながらUPDATEする方法がわからない」
データベースを使っていると、このような場面に遭遇することがよくあります。
複数のテーブルにまたがる情報を使って、効率的にデータを更新する方法を知っていると、とても便利です。
この記事では、SQLで複数テーブルの情報を使ってUPDATEを実行する方法を、データベースの種類別に、初心者でもわかるように詳しく解説していきます。
基本的なUPDATE文をおさらい
単一テーブルのUPDATE
まずは、基本的なUPDATE文の書き方を確認しましょう。
UPDATE テーブル名
SET カラム名 = 新しい値
WHERE 条件;
説明: この基本形では、一つのテーブルの中で、条件に合う行のデータを新しい値に変更します。
例: 商品テーブル(products)で、商品ID「001」の価格を1500円に変更する場合
UPDATE products
SET price = 1500
WHERE product_id = '001';
複数の列を同時に更新
一度に複数の列を更新することもできます。
UPDATE products
SET price = 1500, stock = 100
WHERE product_id = '001';
説明: この例では、価格と在庫数を同時に更新しています。
例: 商品ID「001」の価格を1500円、在庫を100個に変更します。
複数テーブルを使ったUPDATEとは?

どんなときに必要?
複数テーブルを使ったUPDATEが必要になる場面を考えてみましょう:
例1: 商品テーブルにカテゴリ名を追加したい
- 商品テーブル(products)にはカテゴリID(category_id)しかない
- カテゴリテーブル(categories)にカテゴリ名(name)がある
- 商品テーブルにカテゴリ名を追加したい
例2: 売上データに基づいて在庫を調整したい
- 売上テーブル(sales)に販売数量がある
- 商品テーブル(products)の在庫数を販売数量分減らしたい
例3: 顧客情報を最新の住所マスターと同期したい
- 顧客テーブル(customers)に古い住所情報がある
- 住所マスター(addresses)に最新の住所情報がある
SQLの基本的な制限
重要なポイントとして、SQL標準では、UPDATEで直接更新できるのは1つのテーブルのみです。しかし、他のテーブルの情報を参照して更新することは可能です。
MySQL でのJOINを使ったUPDATE

MySQLでは、UPDATE文でJOINを直接使うことができます。
基本的な書き方
UPDATE テーブル1 t1
JOIN テーブル2 t2 ON t1.キー = t2.キー
SET t1.カラム = t2.カラム
WHERE 条件;
実際の例
商品テーブルにカテゴリ名を追加する場合:
UPDATE products p
JOIN categories c ON p.category_id = c.id
SET p.category_name = c.name
WHERE c.type = '食品';
説明: この例では、商品テーブル(products)とカテゴリテーブル(categories)をcategory_idで結合し、食品カテゴリの商品にカテゴリ名を設定しています。
例: 実行前と実行後のデータの変化
-- 実行前のproductsテーブル
product_id | product_name | category_id | category_name
001 | りんご | 1 | NULL
002 | みかん | 1 | NULL
-- categoriesテーブル
id | name | type
1 | 果物 | 食品
-- 実行後のproductsテーブル
product_id | product_name | category_id | category_name
001 | りんご | 1 | 果物
002 | みかん | 1 | 果物
複数の条件でJOIN
より複雑な条件でJOINすることも可能です:
UPDATE products p
JOIN categories c ON p.category_id = c.id
JOIN suppliers s ON p.supplier_id = s.id
SET p.status = '特価'
WHERE c.type = '食品'
AND s.country = '日本'
AND p.price > 1000;
説明: この例では、商品、カテゴリ、仕入先の3つのテーブルを結合して、特定の条件に合う商品のステータスを更新しています。
SQL Server(T-SQL)での方法

SQL Serverでは、FROM句を使ったJOINでUPDATEを実行します。
基本的な書き方
UPDATE テーブル1
SET カラム = 新しい値
FROM テーブル1 t1
JOIN テーブル2 t2 ON t1.キー = t2.キー
WHERE 条件;
実際の例
UPDATE p
SET p.category_name = c.name
FROM products p
JOIN categories c ON p.category_id = c.id
WHERE c.type = '食品';
説明: SQL ServerではFROM句でテーブルを指定し、そこでJOINを行います。MySQLとは書き方が少し違いますが、やっていることは同じです。
例: 商品テーブルの価格を、仕入先の割引率に基づいて更新する場合
UPDATE p
SET p.price = p.price * (1 - s.discount_rate)
FROM products p
JOIN suppliers s ON p.supplier_id = s.id
WHERE s.discount_rate > 0;
LEFT JOINを使った更新
すべての行を対象にしたい場合は、LEFT JOINも使えます:
UPDATE p
SET p.supplier_name = COALESCE(s.name, '不明')
FROM products p
LEFT JOIN suppliers s ON p.supplier_id = s.id;
説明: この例では、すべての商品に対して仕入先名を設定し、仕入先が見つからない場合は「不明」を設定します。
PostgreSQL での方法
PostgreSQLでは、JOINを直接使ったUPDATEはサポートされていないため、サブクエリを使います。
サブクエリを使った基本的な書き方
UPDATE テーブル1
SET カラム = (
SELECT 値 FROM テーブル2
WHERE テーブル2.キー = テーブル1.キー
)
WHERE 条件;
実際の例
UPDATE products
SET category_name = (
SELECT name FROM categories
WHERE categories.id = products.category_id
)
WHERE EXISTS (
SELECT 1 FROM categories
WHERE categories.id = products.category_id
AND categories.type = '食品'
);
説明: この例では、サブクエリを使ってカテゴリテーブルからカテゴリ名を取得し、商品テーブルを更新しています。
例: 売上データに基づいて在庫を更新する場合
UPDATE products
SET stock = stock - (
SELECT SUM(quantity) FROM sales
WHERE sales.product_id = products.product_id
AND sales.sale_date >= '2025-01-01'
)
WHERE EXISTS (
SELECT 1 FROM sales
WHERE sales.product_id = products.product_id
AND sales.sale_date >= '2025-01-01'
);
FROM句を使った方法(PostgreSQL 9.1以降)
PostgreSQL 9.1以降では、FROM句を使った更新も可能です:
UPDATE products
SET category_name = c.name
FROM categories c
WHERE products.category_id = c.id
AND c.type = '食品';
複数の値を一度に更新
複数の値を一度に更新したい場合:
UPDATE products
SET (category_name, supplier_name) = (
SELECT c.name, s.name
FROM categories c, suppliers s
WHERE c.id = products.category_id
AND s.id = products.supplier_id
)
WHERE products.category_id IS NOT NULL
AND products.supplier_id IS NOT NULL;
Oracle での方法

Oracleでも、PostgreSQLと同様にサブクエリを使います。
基本的なサブクエリ更新
UPDATE products
SET category_name = (
SELECT name FROM categories
WHERE id = products.category_id
)
WHERE category_id IN (
SELECT id FROM categories WHERE type = '食品'
);
MERGE文を使った方法
Oracleでは、MERGE文を使ってより効率的に更新できます:
MERGE INTO products p
USING categories c ON (p.category_id = c.id)
WHEN MATCHED AND c.type = '食品' THEN
UPDATE SET p.category_name = c.name;
説明: MERGE文は、条件に合うデータがあれば更新、なければ挿入という処理を一度に行えます。
実践的な使用例
例1: 売上データから商品の人気度を更新
目的: 売上実績に基づいて商品の人気度ランクを更新したい
MySQLの場合:
UPDATE products p
JOIN (
SELECT
product_id,
CASE
WHEN SUM(quantity) >= 100 THEN 'A'
WHEN SUM(quantity) >= 50 THEN 'B'
ELSE 'C'
END as popularity_rank
FROM sales
WHERE sale_date >= '2025-01-01'
GROUP BY product_id
) s ON p.product_id = s.product_id
SET p.popularity_rank = s.popularity_rank;
説明: 売上テーブルの販売数量を集計し、その結果に基づいて商品の人気度ランクを設定します。
例: 100個以上売れた商品は「A」ランク、50個以上は「B」ランク、それ以下は「C」ランクに設定されます。
例2: 顧客の最終購入日を更新
目的: 顧客テーブルに最後に商品を購入した日付を記録したい
SQL Serverの場合:
UPDATE c
SET c.last_purchase_date = o.last_order_date
FROM customers c
JOIN (
SELECT
customer_id,
MAX(order_date) as last_order_date
FROM orders
GROUP BY customer_id
) o ON c.customer_id = o.customer_id;
説明: 注文テーブルから顧客ごとの最新注文日を取得し、顧客テーブルを更新します。
例3: 在庫数の一括調整
目的: 入荷データに基づいて商品の在庫数を一括で調整したい
PostgreSQLの場合:
UPDATE products
SET stock = stock + (
SELECT SUM(received_quantity)
FROM inventory_receipts
WHERE inventory_receipts.product_id = products.product_id
AND received_date = CURRENT_DATE
)
WHERE EXISTS (
SELECT 1 FROM inventory_receipts
WHERE inventory_receipts.product_id = products.product_id
AND received_date = CURRENT_DATE
);
説明: 当日の入荷データを集計し、商品の在庫数に加算します。
複数テーブルの同時更新について

SQLの制限事項
重要なポイントとして、SQL標準では一つのUPDATE文で複数のテーブルを同時に更新することはできません。
代替手段
複数のテーブルを更新したい場合は、以下の方法があります:
方法1: 複数のUPDATE文を順番に実行
-- 1つ目のテーブルを更新
UPDATE table1 SET column1 = value1 WHERE condition1;
-- 2つ目のテーブルを更新
UPDATE table2 SET column2 = value2 WHERE condition2;
方法2: トランザクションを使って同時性を保証
BEGIN TRANSACTION;
UPDATE products
SET stock = stock - 10
WHERE product_id = '001';
UPDATE sales_summary
SET total_sold = total_sold + 10
WHERE product_id = '001';
COMMIT;
説明: トランザクションを使うことで、複数の更新処理を一つの処理単位として扱えます。
例: 商品の在庫を減らすと同時に、売上集計テーブルも更新する場合に使います。
方法3: ストアドプロシージャやファンクションの活用
-- ストアドプロシージャの例(SQL Server)
CREATE PROCEDURE UpdateProductAndSales
@ProductId VARCHAR(10),
@Quantity INT
AS
BEGIN
UPDATE products
SET stock = stock - @Quantity
WHERE product_id = @ProductId;
UPDATE sales_summary
SET total_sold = total_sold + @Quantity
WHERE product_id = @ProductId;
END;
パフォーマンスと最適化
インデックスの重要性
複数テーブルのUPDATEでは、JOIN条件に使用するカラムにインデックスが設定されていることが重要です。
効果的なインデックス例:
-- JOINで使用するカラムにインデックス作成
CREATE INDEX idx_products_category_id ON products(category_id);
CREATE INDEX idx_categories_id ON categories(id);
大量データの処理
大量のデータを更新する場合は、バッチ処理を検討しましょう:
-- 小さなバッチに分けて処理
UPDATE products p
JOIN categories c ON p.category_id = c.id
SET p.category_name = c.name
WHERE c.type = '食品'
AND p.product_id BETWEEN '001' AND '100';
実行計画の確認
複雑なJOINを含むUPDATE文では、実行計画を確認して効率性をチェックしましょう:
-- MySQLの場合
EXPLAIN UPDATE products p
JOIN categories c ON p.category_id = c.id
SET p.category_name = c.name
WHERE c.type = '食品';
注意点とベストプラクティス

JOIN条件の重要性
間違った例:
-- JOIN条件が不適切な例
UPDATE products p
JOIN categories c
SET p.category_name = c.name; -- すべての組み合わせで更新される危険性
正しい例:
-- 適切なJOIN条件
UPDATE products p
JOIN categories c ON p.category_id = c.id -- 適切な結合条件
SET p.category_name = c.name;
データ整合性の確保
外部キー制約の確認: 更新処理が外部キー制約に違反しないよう注意が必要です。
NULL値の処理:
-- NULL値を適切に処理
UPDATE products p
JOIN categories c ON p.category_id = c.id
SET p.category_name = COALESCE(c.name, '未分類')
WHERE p.category_id IS NOT NULL;
バックアップとテスト
本番実行前の準備:
- データのバックアップを取得
- テスト環境で動作確認
- 少量のデータで先行テスト
- 実行計画とパフォーマンスの確認
トランザクション管理
-- 安全な更新処理の例
BEGIN TRANSACTION;
-- 更新前の件数確認
SELECT COUNT(*) FROM products WHERE category_name IS NULL;
-- 更新実行
UPDATE products p
JOIN categories c ON p.category_id = c.id
SET p.category_name = c.name
WHERE c.type = '食品';
-- 更新後の確認
SELECT COUNT(*) FROM products WHERE category_name IS NOT NULL;
-- 問題なければコミット、問題があればロールバック
COMMIT;
-- ROLLBACK;
データベース別構文まとめ表
データベース | 構文パターン | 特徴 | 制限事項 |
---|---|---|---|
MySQL | UPDATE t1 JOIN t2 ON … SET … | 直接JOIN可能 | 更新は1テーブルのみ |
SQL Server | UPDATE t1 SET … FROM t1 JOIN t2 ON … | FROM句でJOIN | 更新は1テーブルのみ |
PostgreSQL | UPDATE t1 SET … WHERE EXISTS(…) | サブクエリ使用 | JOIN構文なし(FROM句は可) |
Oracle | UPDATE t1 SET … WHERE … | サブクエリまたはMERGE | MERGE文が効率的 |
SQLite | UPDATE t1 SET … WHERE EXISTS(…) | サブクエリ使用 | JOIN構文なし |
よくある質問

Q: 複数のテーブルを一度に更新できますか?
A: SQL標準では不可能です。複数のUPDATE文をトランザクション内で実行するか、ストアドプロシージャを使用してください。
Q: JOINしたときに更新対象が予想より多くなってしまいます
A: JOIN条件が不適切な可能性があります。WHERE句で条件を絞り込むか、JOINの結果を事前にSELECT文で確認してください。
Q: パフォーマンスが悪い場合はどうすればいいですか?
A: JOIN条件のカラムにインデックスを作成し、実行計画を確認してください。大量データの場合はバッチ処理も検討してください。
Q: サブクエリとJOINはどちらが良いですか?
A: 一般的にJOINの方が高速ですが、データベースシステムによって異なります。実際のデータで性能測定することをおすすめします。
まとめ
SQL で複数テーブルの情報を使ったUPDATEは、データベース管理において非常に重要な技術です。
重要なポイント
- UPDATE文で直接更新できるのは1つのテーブルのみ
- 他のテーブルの情報はJOINやサブクエリで参照
- データベースによって構文が異なる
- パフォーマンスと安全性に十分注意する
データベース別のアプローチ
- MySQL: JOIN構文が直接使用可能
- SQL Server: FROM句でJOINを記述
- PostgreSQL: サブクエリまたはFROM句を使用
- Oracle: サブクエリまたはMERGE文を活用
ベストプラクティス
- 事前にテスト実行で結果を確認
- インデックスの適切な設定
- トランザクションを使った安全な処理
- 実行計画によるパフォーマンス確認
コメント