find_in_batchesは降順で処理できない

  • 0
    いいね
  • 0
    コメント
    この記事は最終更新日から1年以上が経過しています。

    表題の通り。

    active_recordにfind_in_batchesという便利なメソッドがある。

    通常テーブルのデータを処理する際はeachを使うことが多いと思われるが、そのデータ量が多い場合などはメモリを食ってしまいうまく処理できないことがある。

    こういった場合、find_in_batchesを使えば一度にメモリにロードするデータ量を抑えながら同様の処理ができるので大量のレコードを処理する際に便利だ。

    ただ、以下のコードを見てほしい。

    
    Article.where("id < 100000").order(:id).reverse_order.find_in_batches(batch_size: 1000) do |articles|
      // 適当な処理など...
    end
    
    

    これはArticleテーブルのidが100000以下のレコードを降順で1000件づつ処理しようとしているコードだ。

    一見なにも問題ないように見えるこのコードだが、実際に発行されるSQLは以下のような感じになる。

    // 1ループ目
    SELECT * FROM articles WHERE (id < 100000) AND ORDER BY articles.id DESC LIMIT 1000
    // 2ループ目
    SELECT * FROM articles WHERE (id < 100000) AND (id > 99000) ORDER BY articles.id ASC LIMIT 1000
    // 3ループ目
    SELECT * FROM articles WHERE (id < 100000) AND (id > 99001) ORDER BY articles.id ASC LIMIT 1000
    ...
    

    要は1ループ目以降はorder byが無視されている。

    そこでドキュメントを見ると、

    NOTE: It's not possible to set the order. That is automatically set to ascending on the primary key (“id ASC”) to make the batch ordering work. This also means that this method only works when the primary key is orderable (e.g. an integer or string).

    ということでちゃんとorderが無視されることが書いてある。

    したがって、find_in_batchesは降順で処理できないというわけ。

    ちゃんと読めということになるが案外見落としがちなので気をつけましょうという話でした。

    ちなみにlimitも無視されてbatch_sizeのほうが優先されるので注意。

    関連リンク