MySQLの DELETE には LIMIT OFFSET が使えない!?代わりの方法

最近 MySQL ですごく困ったのがコレ

なぜか DELETE 句に対して OFFSET が使えないこと

これって何でダメなのか不思議ですよね、できてもよさそうなのに

でもルールでそうなってるなら仕方ありません。

ということで、 DELETE句に OFFSET を使う方法とコード例 をまとめみました。

なんで? DELETE 句で OFFSET が使えなくて困った話...

まずこの問題(というか仕様)を発見した経緯について

レコード削除には DELETE  を使うと思うんですが、
そのとき最初の数件だけ飛ばして削除したい、みたいな場面があったんです。

その時にこの問題に気付いてしまいました。

 

例えば次みたいなテーブルとデータを用意したとします。

ツイート情報&ツイート日時を記録する的なテーブルです。

実際にできあがったデータを表示してみると、こんな感じ

tweetsテーブルの中身
id user_name tweet_id tweeted_on
1 hoge 128256 2019-06-01 19:03:10
2 fuga 512 2019-01-17 12:04:19
3 piyo 2048 2019-02-24 20:11:54
4 fuga 81632 2019-05-18 10:22:01
5 piyo 24 2018-04-28 08:35:28

実際は別のデータだけど、こういうテーブルがあったわけです。

 

このテーブルでやりたかったことは、
カラム tweeted_on  が最新のものを数件飛ばし、古いのだけ削除するみたいな処理

そこで次みたいなSQLを書いて実行してみました。

普通に考えれば、これでも通るような気がしますよね?

でも残念、次みたいなエラー が出て怒られてしまいました。

なんでこうなるのか、良く分からないです。

多分 OFFSET  が使えないような事情があるのかも

サブクエリを使えば OFFSET もどきができる!

この OFFSET  が使えない問題

解決策は意外と簡単で、 サブクエリ を使えばいいだけでした。

具体的に書くなら、

  1. サブクエリで OFFSET  を使って絞り込み
  2. そのIDを WHERE  に渡してSELECTする

こういう感じ

 

さっきのケースでいうなら、次みたいなSQLを発行すれば解決です。

ハイライトした6行目に注目

こういう風に、直接じゃなくてサブクエリ内で OFFSET 指定すればよかったんです

そして次がこのクエリを発行した結果(ただし id 列は省略)

tweetsテーブルの中身
id user_name tweet_id tweeted_on
1 hoge 128256 2019-06-01 19:03:10
3 piyo 2048 2019-02-24 20:11:54
4 fuga 81632 2019-05-18 10:22:01

ちゃんと最新の3件を残して、他が全部削除できてます。

レコード削除で X件~Y件の間で削除したい・・・こういう場合はサブクエリが楽なはず

でも IN を使ってるから少し効率が悪いかも...

まあこれで削除できるのはいいんですが・・・

紹介したSQLにある通り、 WHERE id IN (  みたいにINを使っちゃってます。

これだと数万件を超えるレコードがあると、効率悪いかもしれません。

なので、もし効率を求めるなら JOIN  を使うという手もあります。

▼このページで紹介されてたやつ

詳しくは上のオーバーフローページを参照

サブクエリは色々な場面で応用できる

あとちなみに、
ここで紹介した以外にもサブクエリって結構便利です。

▼ちょうど次記事で紹介した使い方とか

複雑なことでも、サブクエリを使うとできるのが便利なとこですね。

多分いろんな場面で応用がきくと思います。

ここまでのまとめ

ここまで書いたみたいに、
MYSQLでは DELETE  にそのまま OFFSET  が使えません。

もし使いたいならサブクエリを挟む必要があることに注意ですね。

以上、DELETE句 で OFFSET  を使えない代わりの方法でした。ではでは

Commentsこの記事についたコメント

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください