「松原ガレッジ」のブログ

大阪府松原市にあるウェブ開発・HP制作をするところ。管理人が思ったことや困ったこと、課題についてまとめていきます。

後からDBにUniqueインデックスをつけるときの前処理のお話〜ダブリのある要素をどうやって見つけるか〜

どうも!
大阪府松原市でウェブを中心としたソフトウェア開発を行っています。
コワーキングスペース松原ガレッジ管理人の五島です。

今日はすでに運用しているサービスのDBのお話。
特定のカラムに対して、ユニーク制約をつける前処理の話を少ししようかな〜と軽快に記事を書いてます!

目次はこちら

ユニークを妨げる要素は存在するか

すでに重複してるデータがある場合、それをまず取り除かないといけないわけです。
pluckを利用して要素をごっそり取ってきた後、配列の処理を行います。

Rubyには配列演算があるので

ary1 = [1, 2, 3, 3]
ary2 = ary1.uniq #=> [1, 2, 3]

ary1 - ary2 #=> OutPutは何?

こんな感じのフローで、重複が存在するIDが探せるのではと思いました。
結果...

[]

まさかの空日!!
そうか...複数入ってる要素も全部取り除いてしまうのか...

パッと思いつく方法だと

ary = [1, 2, 3, 3]
ary.select{|a| ary.count(a) > 1}.uniq

これで重複する値だけ出力できます。
しかし、データ量が多いと計算量が多くなかなか終わりません。オーダーはn2という感じでしょうか。

ここでRubyの便利な関数の登場です。
group_byを使って、値をグルーピング=>2つ以上要素があるものだけ抜き出すという方法を使います。

ary = [1, 2, 3, 3]
ary.group_by{ |e| e }.map { |k, v| v.size > 1 ? k : nil }.compact

これだとざっくりオーダーは2〜3n => nという感じですね!
group_byの方法は、複合ユニークindexを作成する際に二次元配列の形

ary = [[1, 2], [1, 3], [1, 2]]  
ary.group_by{ |e| e }.map { |k, v| v.size > 1 ? k : nil }.compact

#=> [[1,2]]

このような形式でも出力を得られたので、様々なシチュエーションで利用できそうですね

まとめ

というわけで

ary.group_by{ |e| e }.map { |k, v| v.size > 1 ? k : nil }.compact

を使ってみてください