選択カラムでの並び替え

何がしたいのか

例えば、usersテーブルがあり、それはアカウント情報だけを保持しているとする。そして、それを参照するテーブル、students、teachersがあるとする。それぞれ、生徒にしかない情報、先生にしかない情報を持っている。
この時、User一覧を取得する際に、StudentとTeacherの情報更新順にソートして出力したいとする。(StudentとTeacherには、情報更新日時を記録するupdated_atというカラムがある)

何が問題か

この場合、Railsのfind関数を使うと、次のようになる。

User.find(:all,:order => "・・・",:include => [:student,:teacher])

ここでorder_by句が問題になる。

order_by => "student.updated_at","teacher.updated_at"

としても、studentのupdatea_atでソートされて、その結果の中でteacherのupdated_at順にソートされるだけだ。両方のカラムをもつことはないので、結果、最初にstudentがずらりと並び、その後にteacherがずらりと並ぶ結果になる。
また、以下のようにやると、どちらのupdated_atなのか区別できず、ambiguousエラーが発生してしまう。

order_by => "updated_at"

解決策

解決策は、次のようになる。
StudentとTeacherは互いに独立しているので、1人のUserがこの2つの情報を同時に持つことはない。よって、students.updated_atとteachers.updated_atは必ず一方がnullになる。よって、order_by句は次のようになる。

order_by => "COALESCE(student.updated_at,teachers.updated_at)"

COALESCE関数は、引数の中に最初にでてくるnullで内値を返す。よって、このように指定すると、students.updated_atとteachers.updated_atを基準にソートが行なわれる。


ちなみに、MySQLで書くとこんな感じ。

mysql> select users  
LEFT OUTER JOIN students on users.id = students.user_id 
LEFT OUTER JOIN teahcers on users.id = teachers.user_id 
ORDER BY COALESCE(job_searchers.updated_at,recruiters.updated_at);