【MySQL】条件にあてはまるレコードをn個だけ残すように同じ条件にあてはまる他のレコード全てを削除する方法

  • 2023年11月10日
  • SQL

 題の通りです。稀に特定の条件に合致するレコードの数を制限するようなクエリを発行する必要があります。例えば、最新のn件のレコードだけを保持して残りは削除したい、といったような具合です。MySQLでこの処理をするSQLを紹介します。

 まずはSQLで適切に対象を取得する必要があります。なんとなくでDELETEすると事故になりやすいですし、論理削除の場合はUPDATEをする必要があります。対象の取得は例えば次のSQLでできます。

SELECT *
FROM accounts
-- 残したいレコードのID以外を持つレコード全てを取得するようにWHERE NOT IN を使う
WHERE account_id NOT IN (
    -- LIMITを使ったサブクエリをさらにサブクエリで包まないと次のエラーが出る
    -- [42000][1235] This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
    SELECT *
    FROM (
        -- サブクエリで残したいレコードだけ取得する
        SELECT account_id
        FROM accounts
        ORDER BY created_at DESC
        LIMIT 5
    ) AS recent_accounts
)

 このクエリは、accountsテーブルから最新の5件を除いたすべてのレコードを選択します。例のように残したいものだけ得られるサブクエリとそのサブクエリの結果のIDを利用して絞り込みすることによって削除すべきものとそうでないものを識別できるようにします、

 参照クエリが期待通りに動作すれば後は楽です。次のようにSELECT * を DELETE に置き換えるだけです。

DELETE
FROM accounts
WHERE account_id NOT IN (
    SELECT *
    FROM (
        SELECT account_id
        FROM accounts
        ORDER BY created_at DESC
        LIMIT 5
    ) AS recent_accounts
);

 もしLaravelで同じ目的を達成するのであれば次のようにもできます。

$idsToKeep = Account::query()
                ->orderByDesc('created_at')
                ->limit(5)
                ->pluck('id');

Account::query()
  ->whereNotIn('account_id', $idsToKeep)
  ->delete();

 サブクエリをやめて変数に展開し、それを元に削除クエリをつくる形です。残す量が大した量でないのであればメモリにも優しいですし、他の処理で使っている絞り込み機能なども利用できるのでこちらの方が便利です。

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG