PostgreSQL 入門

PostgreSQL におけるデータの削除

テーブルからデータを削除することは、データ管理において極めて重要なプロセスです。PostgreSQLの DELETE 文を使用すると、特定の条件に基づいてテーブルから指定した行(レコード)を削除できます。

本章では、DELETE文の構文、オプション、およびベストプラクティスを深く掘り下げ、データベースの整合性と正確性を維持するために、安全かつ効果的にデータを削除する方法を解説します。

1. 基本的な DELETE 構文 (全レコード削除の危険な操作)

PostgreSQLでデータを削除する最も基本的な構文は以下の通りです。

DELETE FROM テーブル名;

極めて危険: このように条件を一切指定せずに実行すると、指定したテーブル(テーブル名)内の すべての行が削除されます! これは取り返しのつかない結果を招くため、この形式のステートメントを使用する際は細心の注意を払う必要があります。実行(エンターキーを押す)前に、それが本当に望む結果であるか何度も確認してください。

例:
次のようなデータを持つ customers(顧客)テーブルがあると仮定します。

customer_id (顧客ID)first_name (名)last_name (姓)email (メール)
1JohnDoe[email protected]
2JaneSmith[email protected]
3DavidLee[email protected]

もし以下のステートメントを実行すると:

DELETE FROM customers;

その結果、customers テーブルは完全に空になります。すべての行が消失し、空のテーブル構造だけが残ります。

2. WHERE 句によるピンポイントな削除

特定の行を選択的に削除するには、必ず WHERE 句を使用する必要があります。WHERE句は、レコードが削除される前に満たすべき条件を指定します。これがDELETE文を使用する際、最も一般的かつ安全な方法です。

WHERE句を伴う構文は以下の通りです。

DELETE FROM テーブル名
WHERE 条件;

条件 を満たす行だけが削除対象となります。

例:customers テーブルから、customer_id = 3 の顧客だけを削除したい場合は、以下のステートメントを使用します。

DELETE FROM customers
WHERE customer_id = 3;

実行後、customers テーブルは以下のようになります。

customer_idfirst_namelast_nameemail
1JohnDoe[email protected]
2JaneSmith[email protected]

customer_id = 3 のレコードが正常に削除されました。

3. WHERE 句における複数条件と比較

論理演算子(ANDORNOT)を使用して、WHERE句内で複数の条件を組み合わせることができます。

例:論理演算子の使用
姓が "Doe" で、かつ(AND)名が "John" である顧客を削除する場合:

DELETE FROM customers
WHERE last_name = 'Doe' AND first_name = 'John';

姓が "Doe" であるか、または(OR)名が "Jane" である顧客を削除する場合:

DELETE FROM customers
WHERE last_name = 'Doe' OR first_name = 'Jane';

例:比較演算子の使用
WHERE句では、=(等しい)、!=(等しくない)、>(大きい)、<(小さい)、>=(以上)、<=(以下)といった各種比較演算子も使用できます。

例えば、orders(注文)テーブルに order_date(注文日)カラムがあるとします。特定の期日(例:2023年1月1日)より前のすべての注文を削除したい場合は、次のように記述します。

DELETE FROM orders
WHERE order_date < '2023-01-01';

4. 実務シナリオにおける実践ケース

より実際の業務に近いDELETE文の活用シーンを見てみましょう。

4.1 ケース 1:非アクティブユーザーのクリーンアップ

users テーブルに、is_active(アクティブかどうかを示すブーリアン型)カラムがあるとします。非アクティブなすべてのユーザーを一括削除するには次のようにします。

DELETE FROM users
WHERE is_active = FALSE;

4.2 ケース 2:レガシー製品の取り下げ

products(製品)テーブルに last_updated(最終更新日)カラムがあるとします。長い間(例:5年以上)更新されていない製品を削除する場合:

DELETE FROM products
WHERE last_updated < NOW() - INTERVAL '5 year';

4.3 ケース 3:重複する冗長データの削除 (高度な操作)

これは非常に実用的なアドバンスドシナリオです。テーブル内の重複データをクリーンアップする場合、通常はまず重複行を特定し、そのうちの1つを残して他を削除します。ここではサブクエリを活用します。

シナリオ:
ECサイト「ShopSmart」では、システム同期のエラーにより、製品在庫テーブルに時々重複した商品データが発生します。これを自動でクリーンアップする必要があります。
テーブル名は product_inventory で、product_id(プライマリキー)、product_name(製品名)、quantity(数量)が含まれます。ここでは、product_name が同じであれば重複とみなします。

WITH RankedProducts AS (
    SELECT
        product_id,
        product_name,
        quantity,
        ROW_NUMBER() OVER (PARTITION BY product_name ORDER BY product_id) as rn
    FROM
        product_inventory
)
DELETE FROM product_inventory
WHERE product_id IN (SELECT product_id FROM RankedProducts WHERE rn > 1);

コード解説:

  1. CTE (共通テーブル式): WITH RankedProducts AS (...) は、現在のクエリ内だけで有効な一時的な結果セットを定義します。
  2. ウィンドウ関数: ROW_NUMBER() OVER (PARTITION BY product_name ORDER BY product_id)。これは同じ product_name の行を1つの「パーティション(グループ)」にまとめ、product_id の昇順でグループ内の行に番号(rn)を振ります。最も小さいIDが1番になります。
  3. 重複項の抽出: SELECT product_id FROM RankedProducts WHERE rn > 1。これにより、順位が1より大きい行、つまり「重複分」のID(1番目以外のもの)が抽出されます。
  4. 削除の実行: 最後に外側の DELETE 文が、抽出されたIDリストに一致するレコードを削除します。

5. 安全に削除するための黄金律とベストプラクティス

5.1 データバックアップ (Backups)

DELETE文を実行する前(特に大量のデータを操作する場合)は、事前にデータのバックアップを取ることを強く推奨します。これは万が一削除ミスが発生したり、実行中に予期せぬトラブルが起きた際の唯一の復元手段となります。

5.2 トランザクションの利用 (Transactions)

DELETE文をデータベースのトランザクションで囲むことを強く推奨します。トランザクションを使用すると、一連の操作をパッケージ化し、すべて成功した場合は確定(Commit)し、エラーが発生した場合はすべて取り消す(Rollback)ことができます。

BEGIN; -- トランザクション開始

DELETE FROM テーブル名
WHERE 条件;

COMMIT; -- 問題なければ変更を確定

もし実行後(かつ COMMIT 前)に削除ミスに気づいた場合、直ちに以下のコマンドを実行して「時間を巻き戻す」ことができます。

ROLLBACK; -- 直前の削除操作を取り消す

5.3 パフォーマンスの最適化 (Performance)

膨大なデータの削除はシステムリソースを大量に消費します。以下の最適化テクニックを検討してください。

  • インデックスの活用 (Index Usage): WHERE句でフィルタリング条件として使用するカラムにインデックスが作成されていることを確認してください。これにより、データベースが削除対象の行を見つける速度が劇的に向上します。
  • バッチ削除 (Batch Deletion): 数百万行のデータを削除する必要がある場合、一度にすべてを削除しようとせず、数回に分けて削除(例:1回につき1万行など)することを検討してください。これによりデータベースサーバーへの負荷を軽減し、システムハングやロックの発生を防ぎます。
  • メンテナンス (Maintenance): 大規模な削除の後、データベースの内部的には即座にディスク空間が解放されるわけではありません。定期的に VACUUM および ANALYZE コマンドを実行して、空間を回収しクエリ統計情報を更新する習慣をつけましょう。

5.4 権限管理 (Permissions)

DELETE文を実行するデータベースユーザーが、対象テーブルに対して削除権限(DELETE権限)を持っていることを確認してください。権限が不足していると、コマンドはエラーになります。