Rails:最後にnullで注文する


83

私のRailsアプリで、他の人がどのように解決するか知りたいという問題に何度か遭遇しました。

値がオプションである特定のレコードがあるため、一部のレコードには値があり、一部のレコードはその列に対してnullです。

一部のデータベースでその列で並べ替えると、nullが最初に並べ替えられ、一部のデータベースではnullが最後に並べ替えられます。

たとえば、コレクションに属している場合と属していない場合がある写真があります。つまり、どこcollection_id=nilにあるか、どこにあるかcollection_id=1などの写真があります。

私が行う場合はPhoto.order('collection_id desc)SQLiteの上で、私はヌルが最後に取得するが、PostgreSQLの私の最初のヌルを取得します。

これを処理し、任意のデータベースで一貫したパフォーマンスを実現するための、優れた標準のRailsの方法はありますか?

回答:


0

配列を一緒に追加すると、順序が維持されます。

@nonull = Photo.where("collection_id is not null").order("collection_id desc")
@yesnull = Photo.where("collection_id is null")
@wanted = @nonull+@yesnull

http://www.ruby-doc.org/core/classes/Array.html#M000271


2
まあ、私はこのアイデアが好きではありませんが、これはうまくいくと思います。長い間開いたままにして申し訳ありませんが、他の回答が表示されることを期待していました。少し時間をかけて考えてみたのですが、これをフォトモデルのメソッドにできれば、それほど悪くはないと思います。
アンドリュー

mysqlを使用している場合は簡単です。私の解決策を参照してください。
ジェイコブ

そうです、私のものは私が見つけることができる最も不可知論的な方法にすぎなかったと言うべきでした。
エリック

8
where配列を返さないため、これは悪い考えです。ActiveRecord:: Relationshipを返し、結果を配列に強制すると、標準のActiveRecord :: Relationを期待するすべてのものが失敗します(ページ付けなど)。
マイクベサニー2014

1
確かに、利用可能なメソッドはARから抜け出すか、(完全に)移植性がないようです。
エリック

269

私はSQLの専門家ではありませんが、最初にnullの場合は並べ替えてから、並べ替え方法で並べ替えてみませんか。

Photo.order('collection_id IS NULL, collection_id DESC')  # Null's last
Photo.order('collection_id IS NOT NULL, collection_id DESC') # Null's first

PostgreSQLのみを使用している場合は、これも実行できます

Photo.order('collection_id DESC NULLS LAST')  #Null's Last
Photo.order('collection_id DESC NULLS FIRST') #Null's First

普遍的なものが必要な場合(複数のデータベースで同じクエリを使用している場合など)、次を使用できます(@philT提供)

Photo.order('CASE WHEN collection_id IS NULL THEN 1 ELSE 0 END, collection_id')

21
+1、受け入れられた回答よりもはるかに優れており、Arelを介して表現できます
m_x 2012

1
うわー、これは古いコメントです!だから私が言おうとしていたのは:p = Photo.arel_table; Photo.order(p[:collection_id].eq(nil)).order(p[:collection_id].desc)
m_x 2014年

2
NULLS LASTチェーンにあなたを許可しない.last()のRails 4.2のようクエリに。以下に回避策を投稿しました。
Lanny Bose

1
IS NULLNULL行を最後に置くことによる順序付けはなぜですか?あなたはそれがそれらを最初に置くと思うでしょう。
jackocnr 2017

2
@jackocr myguess:IS NULLは、trueの場合は1、falseの場合は0、ソート済み、0は1の前になります...
インテント2017

36

今は2017年ですが、NULLsを優先すべきかどうかについてはまだコンセンサスが得られていません。あなたがそれについて明確にしないと、あなたの結果はDBMSによって異なります。

標準では、NULL以外の値と比較してNULLを並べ替える方法を指定していません。ただし、2つのNULLは同じ順序であると見なされ、NULLはすべてのNULL以外の値の上または下に並べ替える必要があります。

ソース、ほとんどのDBMSの比較

この問題を説明するために、Rails開発に関して最も人気のあるいくつかのケースのリストをまとめました。

PostgreSQL

NULLsの値が最も高くなります。

デフォルトでは、null値はnull以外の値よりも大きいかのようにソートされます。

出典:PostgreSQLドキュメント

MySQL

NULLsの値が最も低くなります。

ORDER BYを実行する場合、ORDER BY ... ASCを実行すると最初にNULL値が表示され、ORDER BY ... DESCを実行すると最後にNULL値が表示されます。

出典:MySQLドキュメント

SQLite

NULLsの値が最も低くなります。

NULL値の行は、通常の値の行よりも昇順で高く、降順では逆になります。

ソース

解決

残念ながら、Rails自体はまだその解決策を提供していません。

PostgreSQL固有

PostgreSQLの場合、非常に直感的に使用できます。

Photo.order('collection_id DESC NULLS LAST') # NULLs come last

MySQL固有

MySQLの場合、マイナス記号を前もって置くことができますが、この機能は文書化されていないようです。数値だけでなく、日付でも機能するように見えます。

Photo.order('-collection_id DESC') # NULLs come last

PostgreSQLおよびMySQL固有

それらの両方をカバーするために、これは機能するように見えます:

Photo.order('collection_id IS NULL, collection_id DESC') # NULLs come last

それでも、これはSQLiteでは機能しません

ユニバーサルソリューション

すべてのDBMSにクロスサポートを提供するにCASEは、@ PhilITによってすでに提案されている、を使用してクエリを作成する必要があります。

Photo.order('CASE WHEN collection_id IS NULL THEN 1 ELSE 0 END, collection_id')

これは、最初に各レコードをCASE結果(デフォルトでは昇順、つまりNULL値が最後の値になる)で最初にソートし、次に。でソートすることを意味しますcalculation_id


14
Photo.order('collection_id DESC NULLS LAST')

私はこれが古いものであることを知っていますが、私はちょうどこのスニペットを見つけました、そしてそれは私のために働きます。


これがどのバージョンのRuby / Railsで機能したかはわかりませんが、Ruby2.5とRails5では機能しないようです。
Tasos Anesiadis

13

入れてマイナス記号をCOLUMN_NAMEの前にと注文方向を逆転。それはmysqlで動作します。詳細

Product.order('something_date ASC') # NULLS came first
Product.order('-something_date DESC') # NULLS came last

10

ショーに少し遅れましたが、それを行うための一般的なSQLの方法があります。いつものようCASEに、救助に。

Photo.order('CASE WHEN collection_id IS NULL THEN 1 ELSE 0 END, collection_id')


6

後世のために、私はに関連するActiveRecordエラーを強調したいと思いました NULLS FIRST

電話をかけようとした場合:

Model.scope_with_nulls_first.last

Railsは無効なSQLを生成しようとするため、を呼び出そうとしますがreverse_order.first、とreverse_orderは互換性がありませんNULLS LAST

PG::SyntaxError: ERROR:  syntax error at or near "DESC"
LINE 1: ...dents"  ORDER BY table_column DESC NULLS LAST DESC LIMIT...

これは、いくつかまだオープンRailsの問題(で数年前に参照された123)。私は次のことを行うことでそれを回避することができました:

  scope :nulls_first, -> { order("table_column IS NOT NULL") }
  scope :meaningfully_ordered, -> { nulls_first.order("table_column ASC") }

2つの注文を連鎖させることにより、有効なSQLが生成されるようです。

Model Load (12.0ms)  SELECT  "models".* FROM "models"  ORDER BY table_column IS NULL DESC, table_column ASC LIMIT 1

唯一の欠点は、この連鎖をスコープごとに実行する必要があることです。


2

私の場合、ASCによる開始日と終了日で行を並べ替える必要がありましたが、end_dateがnullであり、その行が上にある必要がある場合があります。

@invoice.invoice_lines.order('start_date ASC, end_date ASC NULLS FIRST')


-3

データベース自体がNULLがリストの先頭または最後にあるかどうかを解釈するため、データベースタイプ間で一貫した結果が必要な場合は、Rubyでこれを行う必要があるようです。

Photo.all.sort {|a, b| a.collection_id.to_i <=> b.collection_id.to_i}

しかし、それはあまり効率的ではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.