QA@IT

DBの種類に依存せずにcreated_atのdateによるgroup byをするには?

3646 PV

rails group records by dates of created_at - Stack Overflow

上記の質問の回答に下記の記述があるのですが、これを各DBに依存しない形で書く事は出来ないのでしょうか?
DBに依存せずに一つのコードで動作させたい場合は、created_dateのようなdate型のカラムを別に用意するしかないのでしょうか?

  • For MySql you can do:
Model.group("date(table_name.created_at)")
  • For SQLite you can do:
Model.group("strftime('%Y-%m-%d', table_name.created_at)")
  • And for PostgreSQL:
Model.group("table_name.created_at::date")

回答

group byする集計対象自体をメソッド化するという方法があります。

def bucket_for_date
  case ActiveRecord::Base.configurations[Rails.env]['adapter']
  when 'mysql', 'mysql2'
    'date(table_name.created_at)'
  when 'sqlite'
    'strftime('%Y-%m-%d', table_name.created_at)'
  when 'postgres'
    'table_name.created_at::date'
  else
    raise 'unsupported adapter, sorry!'
  end
end

Model.group(bucket_for_date)

adapter判定を毎回評価する必要はないので、起動時に一度だけ実行されるように工夫する余地はあると思います。

このやり方の利点は、将来daysだけでなくweeksやmonthsを扱いたくなったとき、たとえばMySQLだとWEEK関数でモードに3を指定してやらないとISO8601互換にならないとか、いろいろと細かいところで柔軟性が求められるとき、対応しやすいからです。

編集 履歴 (0)
  • コードで条件分岐対応をした場合、対応DBMSによる条件分岐をコードに記載しているために「DBに依存せず」に抵触するかと。

    Railser は SQLで対応が気に食わないみたいなので、質問の回答は削除。

    -
  • 回答ありがとうございます。やはりDBの差異を吸収するコードをどこかに書かないと実現出来ないんですね。致し方無い無難な落とし所かと思います。 -

編集 履歴 (1)
  • 回答ありがとうございます。それだと結局はそれぞれのDBごとに関数を作成しなければなりませんよね?`Model.group( :created_at => :date )`←こういう事がやりたいのですが、やはりどこかにDBの種類を吸収する関数なりコードなりを書かなければ実現出来ないのでしょうか。 -
  • だとすると、全てのデータベースで実行できる日付変換関数を作ればいいだけです。
    -
ウォッチ

この質問への回答やコメントをメールでお知らせします。