#環境
rails 5.1.4
ruby 2.5.0
#背景
Railsでリリース前のアプリを作っていて、Tagモデルに後からunique制約を入れたのですが、元々のデータで重複がたくさんあり、それを消す時にどうすればいいか悩みました。
最初は下記の記事を参考に削除しようとしたのですが、created_atが重複して完全には消せなかったので、IDをキーに削除しました。IDをキーで消したい人の参考になれば。それ以外のキーで消したい場合は、下記記事を参考ください。
【Ruby on Rails】重複しているレコードを取得する【MySQL】
http://techblog.kyamanak.com/entry/2017/10/18/215532
#方法
まずは下記のコードで重複がどれだけあるか確認。今回はTagモデルのnameの重複を確認します。
そうすると重複しているレコードがズラズラ出て来ます。
pry(main)> Tag.group(:name).count.sort_by(&:second).reverse
=> [["aaa", 4],
["bbb", 3],
["ccc", 2],
["ddd", 2],
["eee", 2],
etc...
順調に重複しているのがわかると思います。
方針としては上記記事にもある通り下記の方針で削除します。
・重複しているレコードの中で、残したいレコードのIDを取得する。
・重複しているレコードの中で、残したいレコード以外を削除する。
その中で上記記事ではcreated_atを基準に削除されていました。
しかし私の場合は、created_atが被っていた為、削除ができませんでした。
そのためIDをキーに削除することにしました。
IDをキーにすると書くコードを一つ少なくすることができます。
結論的には下記で消すことができます。
pry(main)> hash = Tag.group(:name).having('count(*) >= 2').maximum(:id)
=> {"aaa"=>2202,
"bbb"=>3361,
"ccc"=>2699,
"ddd"=>3960,
"eee"=>2212,
etc...
pry(main)> Tag.where(name: hash.keys).where.not(id: hash.values).destroy_all
最後に消せたかどうかを確認すると、
pry(main)> Tag.group(:name).count.sort_by(&:second).reverse
=> [["aaa", 1],
["bbb", 1],
["ccc", 1],
["ddd", 1],
["eee", 1],
etc...
で消せたことが確認できました。