黒魔法使いの弟子

Ruby & Javascript & Hack

ActiveRecordでのdistinctについて

  • あるModelから、
  • user.idで抽出し、
  • field_nameでDistinctしたレコードを、
  • created_atの降順で、
  • 5つ欲しかった

ので、以下のように実装してみた。

models =
  SomeModel.find(:all,
            :select => "distinct field_name",
            :limit => 5, 
            :order => "created_at desc", 
            :conditions => ["user_id = ?", user.id])

ローカル環境(MySQL)では、こいつでもまあ良かったのだが、
公私共にお世話になっているHerokuではPostgresqlを使っている様子で、
Distinct の文法が若干異なるのでエラーを出された。


なんで、以下のようにして解決した。

if connection.supports_count_distinct?
  select_str = 
    connection.distinct(:field_name, "created_at desc")
  models =
    SomeModel.find(:all,
              :select => select_str,
              :limit => 5, 
              :conditions => ["user_id = ?", user.id])
end

細かい点

connection.supports_count_distinct?

こいつは、各データベース用のアダプタでDistinctが実装されているか確認するメソッド。
connectionってのは、以下を参照のこと。


参考:
Ruby on Rails - ActiveRecord -

ありえるえりあ

  connection.distinct(:field_name, "created_at desc")

こいつで、各データベースがDistinctを拡張しているので、
言われたとおりにデータを渡す。


参考:


というカンジ。

ついでに

なお、Distinctが実装されていない場合も考えて
以下の実装も追加した。

temp_models = 
  SomeModel.find(:all,
            :order => "created_at desc", 
            :conditions => ["user_id = ?", user.id])

models = 
  temp_models.map{ |i| i }.uniq[0..5]

まとめ

最終的にはこんなカンジ。

class SomeModel < ActiveRecord::Base
  def self.recent(user)
    if connection.supports_count_distinct?
      select_str = 
        connection.distinct(:field_name, "created_at desc")
      models =
        SomeModel.find(:all,
                  :select => select_str,
                  :limit => 5, 
                  :conditions => ["user_id = ?", user.id])
    else
      temp_models = 
        SomeModel.find(:all,
                  :order => "created_at desc", 
                  :conditions => ["user_id = ?", user.id])

      models = 
        temp_models.map{ |i| i }.uniq[0..5]
    end
  end
end