In meiner Rails-App bin ich ein paar Mal auf ein Problem gestoßen, das ich gerne wissen möchte, wie andere Leute es lösen:
Ich habe bestimmte Datensätze, bei denen ein Wert optional ist, daher haben einige Datensätze einen Wert und einige sind für diese Spalte null.
Wenn ich in einigen Datenbanken nach dieser Spalte ordne, werden die Nullen zuerst und in einigen Datenbanken zuletzt die Nullen sortiert.
Zum Beispiel habe ich Fotos, die zu einer Sammlung gehören können oder nicht, dh es gibt einige Fotos wo collection_id=nil
und einige wo collection_id=1
usw.
Wenn ich das Photo.order('collection_id desc)
dann auf SQLite mache, bekomme ich die Nullen zuletzt, aber auf PostgreSQL bekomme ich zuerst die Nullen.
Gibt es eine nette Standard-Rails-Methode, um damit umzugehen und eine konsistente Leistung in jeder Datenbank zu erzielen?
where
kein Array zurückgegeben wird, sondern eine ActiveRecord :: Relation zurückgegeben wird. Wenn Sie die Ergebnisse in ein Array zwingen, schlägt alles fehl, was eine standardmäßige ActiveRecord :: Relation erwartet (z. B. Paginierung).Ich bin kein SQL-Experte, aber warum nicht einfach sortieren, wenn etwas zuerst null ist, und dann sortieren, wie Sie es sortieren wollten.
Wenn Sie nur PostgreSQL verwenden, können Sie dies auch tun
Wenn Sie etwas Universelles möchten (z. B. wenn Sie dieselbe Abfrage in mehreren Datenbanken verwenden, können Sie diese verwenden (mit freundlicher Genehmigung von @philT).
quelle
p = Photo.arel_table; Photo.order(p[:collection_id].eq(nil)).order(p[:collection_id].desc)
NULLS LAST
dass Sie ab.last()
Rails 4.2 nicht mehr mit Ihrer Abfrage verketten können. Ich habe unten eine Problemumgehung veröffentlicht.IS NULL
die NULL-Zeilen zuletzt bestellt? Sie würden denken, es würde sie an die erste Stelle setzen.Auch wenn es jetzt 2017 ist, gibt es noch keinen Konsens darüber, ob
NULL
s Vorrang haben sollte. Ohne dass Sie dies explizit angeben, variieren Ihre Ergebnisse je nach DBMS.Um das Problem zu veranschaulichen, habe ich eine Liste der beliebtesten Fälle für die Rails-Entwicklung zusammengestellt:
PostgreSQL
NULL
s haben den höchsten Wert.MySQL
NULL
s haben den niedrigsten Wert.SQLite
NULL
s haben den niedrigsten Wert.Lösung
Leider bietet Rails selbst noch keine Lösung dafür.
PostgreSQL-spezifisch
Für PostgreSQL können Sie ganz intuitiv Folgendes verwenden:
Photo.order('collection_id DESC NULLS LAST') # NULLs come last
MySQL-spezifisch
Für MySQL könnten Sie das Minuszeichen in den Vordergrund stellen, diese Funktion scheint jedoch nicht dokumentiert zu sein. Scheint nicht nur mit numerischen Werten zu arbeiten, sondern auch mit Datumsangaben.
Photo.order('-collection_id DESC') # NULLs come last
PostgreSQL- und MySQL-spezifisch
Um beide abzudecken, scheint dies zu funktionieren:
Photo.order('collection_id IS NULL, collection_id DESC') # NULLs come last
Dieser funktioniert jedoch nicht in SQLite.
Universelle Lösung
Um Cross-Support für alle DBMS bereitzustellen, müssen Sie eine Abfrage schreiben
CASE
, die bereits von @PhilIT vorgeschlagen wurde:Photo.order('CASE WHEN collection_id IS NULL THEN 1 ELSE 0 END, collection_id')
Dies bedeutet, dass jeder Datensatz zuerst nach
CASE
Ergebnissen sortiert wird (standardmäßig in aufsteigender Reihenfolge, dh dieNULL
Werte sind die letzten), dann nachcalculation_id
.quelle
Ich weiß, dass dies ein alter ist, aber ich habe gerade diesen Ausschnitt gefunden und er funktioniert für mich.
quelle
Setzen Sie ein Minuszeichen vor den Spaltennamen und kehren Sie die Reihenfolge um. Es funktioniert auf MySQL. Mehr Details
quelle
Etwas spät zur Show, aber es gibt eine generische SQL-Methode, um dies zu tun. Wie immer
CASE
zur Rettung.quelle
Der einfachste Weg ist zu verwenden:
.order('name nulls first')
quelle
last
scheint einen fehlerhaften DESC einzufügen.Aus Gründen der Nachwelt wollte ich einen ActiveRecord-Fehler in Bezug auf hervorheben
NULLS FIRST
.Wenn Sie versuchen anzurufen:
Rails versucht aufzurufen
reverse_order.first
undreverse_order
ist nicht kompatibelNULLS LAST
, da es versucht, die ungültige SQL zu generieren:Dies wurde vor einigen Jahren in einigen noch offenen Rails-Ausgaben ( eins , zwei , drei ) erwähnt. Ich konnte es umgehen, indem ich Folgendes tat:
Es scheint, dass durch Verketten der beiden Aufträge gültiges SQL generiert wird:
Der einzige Nachteil ist, dass diese Verkettung für jeden Bereich durchgeführt werden muss.
quelle
In meinem Fall brauchte ich Sortierzeilen nach Start- und Enddatum nach ASC, aber in einigen Fällen war end_date null und diese Zeilen sollten oben stehen, habe ich verwendet
@invoice.invoice_lines.order('start_date ASC, end_date ASC NULLS FIRST')
quelle
Holen Sie sich zuerst Fotos, bei denen
collection_id
nicht null ist, und ordnen Sie siecollection_id
absteigend an.Holen Sie sich dann Fotos, wo
collection_id
null ist, und fügen Sie sie hinzu.quelle
Es scheint, als müssten Sie dies in Ruby tun, wenn Sie konsistente Ergebnisse für alle Datenbanktypen wünschen, da die Datenbank selbst interpretiert, ob die NULL-Werte am Anfang oder Ende der Liste stehen oder nicht.
Das ist aber nicht sehr effizient.
quelle