はじめに
データベースを運用していると、登録済みのデータを修正する場面は日常的に発生します。顧客情報の住所変更、商品価格の更新、在庫数の調整など、データの変更はデータベース操作の中でも特に重要な基本スキルです。
PostgreSQLでは、UPDATE文を使用してテーブル内の既存データを変更します。本記事では、UPDATE文の基本的な書き方から、複数列の同時更新、条件を指定した部分更新、さらには実務で役立つ応用テクニックまで、段階的に解説していきます。
初心者の方でも安心して学べるよう、豊富な実例とともに丁寧に説明しますので、ぜひ最後までお読みください。
この記事で分かること
本記事を読むことで、以下の知識とスキルが身につきます。
- UPDATE文の基本構文と使い方
- WHERE句を使った条件指定による部分的なデータ更新
- 複数の列を同時に更新する方法
- 計算式を使ったデータの更新テクニック
- サブクエリを活用した高度な更新方法
- UPDATE文の実行時の注意点とトラブルシューティング
- トランザクションを使った安全なデータ更新手法
PostgreSQLでのデータ変更操作を完全にマスターできる内容となっています。
稼働環境
本記事の内容は、以下の環境で動作確認を行っています。
- OS:Windows Server 2025(64bit)
- データベース:PostgreSQL 16
- クライアントツール:psql(PostgreSQL付属のコマンドラインツール)
他のバージョンのPostgreSQLでも基本的な動作は同じですが、一部の機能や構文に違いがある場合があります。
UPDATE文の基本構文
UPDATE文は、テーブル内の既存データを変更するためのSQL文です。基本的な構文は以下の通りです。
UPDATE テーブル名
SET 列名 = 新しい値
WHERE 条件;各要素の意味を確認しましょう。
- UPDATE テーブル名:更新対象のテーブルを指定します
- SET 列名 = 新しい値:どの列をどのような値に変更するかを指定します
- WHERE 条件:どの行を更新するかの条件を指定します(省略すると全行が更新されます)
WHERE句の指定は非常に重要です。省略すると、テーブル内のすべての行が更新されてしまうため、注意が必要です。
実践:基本的なUPDATE操作
実際にUPDATE文を使ってデータを変更してみましょう。まず、サンプルテーブルを作成します。
サンプルテーブルの作成
-- 商品マスタテーブルの作成
CREATE TABLE products (
product_id SERIAL PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
price INTEGER NOT NULL,
stock INTEGER DEFAULT 0,
category VARCHAR(50)
);
-- サンプルデータの挿入
INSERT INTO products (product_name, price, stock, category) VALUES
('ノートパソコン', 89800, 15, '電子機器'),
('ワイヤレスマウス', 2980, 50, '周辺機器'),
('USB Type-Cケーブル', 980, 100, '周辺機器'),
('モニター27インチ', 35800, 8, '電子機器'),
('キーボード', 5980, 30, '周辺機器');単一の値を更新する
最もシンプルなUPDATE文から始めましょう。特定の商品の価格を変更します。
-- product_idが1の商品の価格を変更
UPDATE products
SET price = 79800
WHERE product_id = 1;このUPDATE文により、ノートパソコンの価格が89,800円から79,800円に変更されます。
更新結果の確認
SELECT * FROM products WHERE product_id = 1;実行結果を確認すると、価格が正しく更新されていることが分かります。
複数の列を同時に更新する
UPDATE文では、カンマで区切ることで複数の列を同時に更新できます。これにより、1回のSQL実行で複数の変更を適用できます。
-- 価格と在庫を同時に更新
UPDATE products
SET price = 2480,
stock = 45
WHERE product_id = 2;この方法は、関連する複数の項目を一度に更新する場合に便利です。データの整合性を保ちながら効率的に更新できます。
WHERE句を使った条件指定
WHERE句を使用することで、特定の条件に合致する行だけを更新できます。これはUPDATE文の最も重要な機能の一つです。
特定のカテゴリの商品を一括更新
-- 周辺機器カテゴリの商品を10%値上げ
UPDATE products
SET price = price * 1.10
WHERE category = '周辺機器';この例では、計算式を使って価格を現在の値の1.1倍に更新しています。
複数条件での更新
AND、ORを使って複雑な条件を指定できます。
-- 在庫が10個以下かつ価格が10,000円以上の商品の在庫を補充
UPDATE products
SET stock = stock + 20
WHERE stock <= 10 AND price >= 10000;IN句を使った更新
-- 特定の複数商品をまとめて更新
UPDATE products
SET category = 'PC関連'
WHERE product_id IN (1, 2, 5);計算式を使った更新
UPDATE文のSET句では、既存の値を使った計算式を指定できます。これにより、相対的な値の変更が可能になります。
在庫数の増減
-- 在庫を10個追加
UPDATE products
SET stock = stock + 10
WHERE product_id = 3;
-- 在庫を5個減らす
UPDATE products
SET stock = stock - 5
WHERE product_id = 4;パーセンテージでの価格変更
-- 全商品を5%値下げ
UPDATE products
SET price = ROUND(price * 0.95);
-- 電子機器カテゴリのみ3%値上げ
UPDATE products
SET price = ROUND(price * 1.03)
WHERE category = '電子機器';ROUND関数を使うことで、計算結果を整数に丸めることができます。
NULLの扱い方
PostgreSQLでは、列にNULL値を設定したり、NULL値を他の値に変更したりできます。
NULL値への更新
-- カテゴリをNULLに設定
UPDATE products
SET category = NULL
WHERE product_id = 1;NULL値から他の値への更新
-- NULLのカテゴリに'未分類'を設定
UPDATE products
SET category = '未分類'
WHERE category IS NULL;NULL値の比較には、IS NULL や IS NOT NULL を使用する点に注意してください。
サブクエリを使った高度な更新
サブクエリを使用することで、他のテーブルや集計結果に基づいた更新が可能になります。
別テーブルの値を参照した更新
まず、関連テーブルを作成します。
-- 価格改定テーブルの作成
CREATE TABLE price_updates (
product_id INTEGER,
new_price INTEGER
);
INSERT INTO price_updates VALUES
(1, 85000),
(2, 3200),
(3, 1100);サブクエリを使って更新します。
-- 価格改定テーブルの値で商品価格を更新
UPDATE products
SET price = (
SELECT new_price
FROM price_updates
WHERE price_updates.product_id = products.product_id
)
WHERE product_id IN (SELECT product_id FROM price_updates);集計結果を使った更新
-- カテゴリ内の平均価格を計算して、新しい列に保存
ALTER TABLE products ADD COLUMN category_avg_price INTEGER;
UPDATE products p1
SET category_avg_price = (
SELECT AVG(price)::INTEGER
FROM products p2
WHERE p2.category = p1.category
);RETURNING句で更新結果を取得
PostgreSQLでは、RETURNING句を使うことで、更新されたデータを即座に取得できます。これは非常に便利な機能です。
-- 更新と同時に更新後のデータを取得
UPDATE products
SET price = 6500
WHERE product_id = 5
RETURNING *;
-- 特定の列のみを取得
UPDATE products
SET stock = stock - 1
WHERE product_id = 2
RETURNING product_id, product_name, stock;RETURNING句を使うことで、UPDATE文とSELECT文を別々に実行する必要がなくなり、効率的なデータ操作が可能になります。
FROM句を使った複数テーブル参照
PostgreSQLでは、FROM句を使って他のテーブルを参照しながら更新できます。
-- 在庫補充履歴テーブルを作成
CREATE TABLE restock_log (
product_id INTEGER,
restock_amount INTEGER
);
INSERT INTO restock_log VALUES (1, 5), (3, 20), (4, 3);
-- FROM句を使った更新
UPDATE products
SET stock = stock + restock_log.restock_amount
FROM restock_log
WHERE products.product_id = restock_log.product_id;この方法は、JOINを使った更新よりも直感的で読みやすいコードになります。
トランザクションを使った安全な更新
重要なデータを更新する際は、トランザクションを使用することで、問題が発生した場合に変更を取り消すことができます。
-- トランザクション開始
BEGIN;
-- データの更新
UPDATE products
SET price = price * 1.05
WHERE category = '電子機器';
-- 結果を確認
SELECT * FROM products WHERE category = '電子機器';
-- 問題なければコミット、問題があればロールバック
COMMIT;
-- または
-- ROLLBACK;トランザクションを使うことで、データの整合性を保ちながら安全に更新作業を行えます。
UPDATE文使用時の注意点
UPDATE文を使用する際は、以下の点に注意しましょう。
WHERE句の省略に注意
WHERE句を省略すると、テーブル内のすべての行が更新されます。これは意図しない大量更新につながる可能性があります。
-- 危険:全商品の価格が0になってしまう
UPDATE products SET price = 0;
-- 安全:特定の商品のみ更新
UPDATE products SET price = 0 WHERE product_id = 999;更新前の確認
大規模な更新を行う前は、まずSELECT文で対象データを確認しましょう。
-- 更新対象を確認
SELECT * FROM products WHERE category = '周辺機器';
-- 確認後に更新
UPDATE products
SET price = price * 0.9
WHERE category = '周辺機器';データ型の一致
SET句で指定する値は、列のデータ型と一致している必要があります。
-- エラー:文字列を数値型の列に設定
UPDATE products SET price = 'ABC' WHERE product_id = 1;
-- 正しい:数値を設定
UPDATE products SET price = 1000 WHERE product_id = 1;制約の確認
NOT NULL制約やCHECK制約がある列を更新する場合、制約に違反しないよう注意が必要です。
-- エラー:NOT NULL制約違反
UPDATE products SET product_name = NULL WHERE product_id = 1;
-- 正しい:有効な値を設定
UPDATE products SET product_name = '新製品名' WHERE product_id = 1;実務で役立つUPDATE文のパターン
実際の業務でよく使われるUPDATE文のパターンをいくつか紹介します。
タイムスタンプの自動更新
-- 更新日時列を追加
ALTER TABLE products ADD COLUMN updated_at TIMESTAMP;
-- 更新時に現在時刻を自動設定
UPDATE products
SET price = 12000,
updated_at = CURRENT_TIMESTAMP
WHERE product_id = 1;条件分岐を使った更新(CASE式)
-- 在庫数に応じて異なる処理を行う
UPDATE products
SET price = CASE
WHEN stock < 10 THEN price * 1.1 -- 在庫少:値上げ
WHEN stock > 50 THEN price * 0.95 -- 在庫多:値下げ
ELSE price -- それ以外:据え置き
END;文字列の置換
-- カテゴリ名の一部を置換
UPDATE products
SET category = REPLACE(category, '機器', 'デバイス');連番の再採番
-- 一時的なシーケンス用の変数を使った連番更新
WITH numbered AS (
SELECT product_id, ROW_NUMBER() OVER (ORDER BY product_id) as new_id
FROM products
)
UPDATE products
SET product_id = numbered.new_id
FROM numbered
WHERE products.product_id = numbered.product_id;パフォーマンスを考慮した更新
大量のデータを更新する場合、パフォーマンスに配慮する必要があります。
インデックスの活用
WHERE句で使用する列にインデックスが設定されていると、更新対象の検索が高速化されます。
-- category列にインデックスを作成
CREATE INDEX idx_products_category ON products(category);
-- インデックスを活用した高速更新
UPDATE products
SET price = price * 1.05
WHERE category = '電子機器';バッチ更新
大量データを更新する場合は、一度に更新せず、分割して実行することを検討しましょう。
-- 1000件ずつ更新
UPDATE products
SET stock = stock + 10
WHERE product_id IN (
SELECT product_id FROM products
WHERE stock < 50
LIMIT 1000
);つまずきポイントと解決策
初心者がUPDATE文で陥りやすい問題と、その解決方法を紹介します。
問題1:WHERE句を忘れて全データが更新された
症状:意図せずテーブル全体のデータが変更されてしまった
原因:WHERE句を指定せずにUPDATE文を実行した
解決策:
- トランザクションを使用して、問題があればROLLBACKで元に戻す
- 更新前に必ずSELECT文で対象データを確認する習慣をつける
- 本番環境では、常にBEGINでトランザクションを開始してから更新する
-- 安全な更新手順
BEGIN;
SELECT * FROM products WHERE category = '周辺機器'; -- 確認
UPDATE products SET price = price * 1.1 WHERE category = '周辺機器';
SELECT * FROM products WHERE category = '周辺機器'; -- 結果確認
COMMIT; -- 問題なければコミット問題2:計算結果が予想と異なる
症状:価格を1.1倍にしたはずが、小数点以下が切り捨てられた
原因:INTEGER型の列で計算すると、結果も整数になる
解決策:
- 計算時にキャストして浮動小数点演算を行う
- ROUND関数で適切に丸める
-- 問題のあるコード
UPDATE products SET price = price * 1.1; -- 整数演算になる
-- 正しいコード
UPDATE products SET price = ROUND(price * 1.1); -- 浮動小数点で計算後に丸める問題3:サブクエリがNULLを返して意図しない結果に
症状:サブクエリを使った更新で、一部のデータがNULLになった
原因:サブクエリが該当データを見つけられなかった
解決策:
- COALESCE関数でNULLの場合のデフォルト値を指定
- WHERE句でサブクエリに該当するデータのみを更新対象にする
-- 問題のあるコード
UPDATE products
SET price = (SELECT new_price FROM price_updates WHERE price_updates.product_id = products.product_id);
-- 正しいコード
UPDATE products
SET price = COALESCE(
(SELECT new_price FROM price_updates WHERE price_updates.product_id = products.product_id),
price -- NULLの場合は現在の価格を維持
)
WHERE product_id IN (SELECT product_id FROM price_updates);問題4:更新が反映されない
症状:UPDATE文を実行してもデータが変わらない
原因:
- WHERE句の条件が厳しすぎて対象データが0件
- トランザクション内で実行したがCOMMITしていない
- 別のセッションで確認している
解決策:
- 更新件数を確認する(psqlでは自動表示される)
- トランザクション使用時は必ずCOMMITする
- 同じセッションで結果を確認する
-- 更新件数の確認方法
UPDATE products SET price = 10000 WHERE product_id = 999;
-- 結果:UPDATE 0 ← 0件更新された(対象データなし)よくある質問(FAQ)
Q1: UPDATE文で複数のテーブルを同時に更新できますか?
A: PostgreSQLの標準的なUPDATE文では、1つのテーブルしか更新できません。複数のテーブルを更新する場合は、複数のUPDATE文を実行するか、トリガーや関数を使用します。ただし、FROM句を使って他のテーブルを参照することは可能です。
-- 複数テーブルの更新例(トランザクション内で実行)
BEGIN;
UPDATE products SET price = 10000 WHERE product_id = 1;
UPDATE order_details SET unit_price = 10000 WHERE product_id = 1;
COMMIT;Q2: UPDATE文の実行を取り消すことはできますか?
A: トランザクション内でUPDATE文を実行した場合、COMMITする前であればROLLBACKで取り消すことができます。しかし、トランザクションを使用せずに実行してしまった場合、基本的には元に戻すことはできません。そのため、重要なデータを更新する際は必ずトランザクションを使用しましょう。
BEGIN;
UPDATE products SET price = 0; -- 間違った更新
ROLLBACK; -- 取り消しQ3: WHERE句なしでUPDATE文を実行してしまいました。どうすればいいですか?
A: トランザクション内で実行していた場合はROLLBACKで元に戻せます。そうでない場合は、バックアップから復元するか、データを手動で修正する必要があります。このような事故を防ぐため、本番環境では常にトランザクションを使用し、更新前にSELECT文で確認する習慣をつけましょう。
Q4: 大量のデータを更新する際の注意点は?
A: 大量データの更新では以下の点に注意してください。
- テーブルロックが発生し、他のユーザーの操作に影響する可能性がある
- トランザクションログが大量に発生する
- 処理に時間がかかる
対策として、データを分割して更新する、オフピーク時間に実行する、必要に応じてインデックスを一時的に削除するなどの方法があります。
Q5: UPDATE文でエラーが発生した場合、どこまで更新されますか?
A: UPDATE文の実行中にエラーが発生した場合、その文全体がロールバックされ、1件も更新されません(原子性)。ただし、トランザクション内で複数のUPDATE文を実行している場合、エラーが発生した文だけでなく、トランザクション全体をロールバックする必要があります。
まとめ
本記事では、PostgreSQL 16におけるUPDATE文を使ったデータ変更の方法について、基礎から応用まで幅広く解説しました。
重要なポイントを振り返りましょう。
- UPDATE文は既存データを変更するための基本的なSQL文である
- WHERE句の指定は必須レベルで重要(省略すると全行が更新される)
- SET句では計算式やサブクエリを使った柔軟な更新が可能
- RETURNING句を使えば更新結果を即座に取得できる
- トランザクションを使うことで安全にデータを更新できる
- 更新前には必ずSELECT文で対象データを確認する習慣をつける
UPDATE文は、データベース操作の中でも特に注意深く扱う必要があります。誤った更新は重大な問題を引き起こす可能性があるため、本記事で紹介した安全な更新手法を実践してください。
データの変更操作をマスターすることで、より実践的なデータベース管理が可能になります。次のステップとして、DELETE文やトランザクション管理について学習することをお勧めします。