Schienen: Wählen Sie eindeutige Werte aus einer Spalte aus

238

Ich habe bereits eine funktionierende Lösung, würde aber gerne wissen, warum dies nicht funktioniert:

ratings = Model.select(:rating).uniq
ratings.each { |r| puts r.rating }

Es werden eindeutige Werte ausgewählt, aber nicht gedruckt. Es werden alle Werte einschließlich der Duplikate gedruckt. Und es ist in der Dokumentation: http://guides.rubyonrails.org/active_record_querying.html#selecting-specific-fields

alexandrecosta
quelle
2
Ein weiteres Beispiel mit uniq stackoverflow.com/questions/8369812/…
manish nautiyal

Antworten:

449
Model.select(:rating)

Das Ergebnis ist eine Sammlung von ModelObjekten. Keine einfachen Bewertungen. Und aus uniqSicht sind sie völlig anders. Sie können dies verwenden:

Model.select(:rating).map(&:rating).uniq

oder dies (am effizientesten)

Model.uniq.pluck(:rating)

# rails 5+
Model.distinct.pluck(:rating)

Aktualisieren

Ab Rails 5.0.0.1 funktioniert es anscheinend nur bei Abfragen der obersten Ebene, wie oben. Funktioniert nicht mit Sammlungsproxys (z. B. "has_many" -Relationen).

Address.distinct.pluck(:city) # => ['Moscow']
user.addresses.distinct.pluck(:city) # => ['Moscow', 'Moscow', 'Moscow']

In diesem Fall nach der Abfrage deduplizieren

user.addresses.pluck(:city).uniq # => ['Moscow']
Sergio Tulentsev
quelle
Ich habe eine: group (: Bewertung) .collect {| r | r.rating} Seit map == collect, wo kann ich über diese von Ihnen verwendete Sintax lesen (&: Bewertung)? Ich sehe das nicht in Rubys Dokumentation.
Alexandrecosta
@ user1261084: Siehe Symbol # to_proc , um .map (&: Bewertung) zu verstehen. PragDave erklärt
Dbenhur
63
Es ist erwähnenswert, dass dies Model.uniq.pluck(:rating)der effizienteste Weg ist - dies generiert SQL, das verwendet SELECT DISTINCTund nicht .uniqauf ein Array
angewendet wird
23
In Rails 5 Model.uniq.pluck(:rating)wirdModel.distinct.pluck(:rating)
neurodynamisch
2
Wenn Sie eindeutige Werte aus der Beziehung has_many auswählen möchten, können Sie dies jederzeit tunModel.related_records.group(:some_column).pluck(:some_column)
Krzysztof Karski
92

Wenn Sie verwenden möchten Model.select, können Sie auch einfach verwenden DISTINCT, da nur die eindeutigen Werte zurückgegeben werden. Dies ist besser, da dies bedeutet, dass weniger Zeilen zurückgegeben werden und etwas schneller sein sollte als die Rückgabe mehrerer Zeilen und die Anweisung von Rails, die eindeutigen Werte auszuwählen.

Model.select('DISTINCT rating')

Dies setzt natürlich DISTINCTvoraus, dass Ihre Datenbank das Schlüsselwort versteht und die meisten sollten.

Kakubei
quelle
6
Model.select("DISTINCT rating").map(&:rating)um eine Reihe nur der Bewertungen zu erhalten.
Kris
Ideal für diejenigen mit Legacy-Apps, die Rails 2.3 verwenden
Mikey
3
Ja, das funktioniert fantastisch - aber es gibt nur das DISTINCT-Attribut zurück. Wie können Sie das gesamte Modellobjekt zurückgeben, solange es eindeutig ist? Damit Sie in Fällen, in denen das Attribut eindeutig ist, Zugriff auf alle Attribute im Modell haben.
zero_cool
@Jackson_Sandland Wenn Sie ein Modellobjekt möchten, muss dieses aus einem Datensatz in der Tabelle instanziiert werden. Sie wählen jedoch nicht nur einen eindeutigen Wert für einen Datensatz aus (möglicherweise mehrere Datensätze).
Benissimo
69

Das funktioniert auch.

Model.pluck("DISTINCT rating")
Nat
quelle
Ich glaube, Zupfen ist Ruby 1.9.x und höher. Jeder, der eine frühere Version verwendet, wird sie nicht haben. Wenn Sie in 1.9x und höher sind, sagen die Ruby-Dokumente, dass dies auch funktioniert: Model.uniq.pluck (: Bewertung)
Kakubei
6
pluckist eine reine Rails> 3.2-Methode, die keine Abhängigkeit von Ruby 1.9.x hat. Siehe apidock.com/rails/v3.2.1/ActiveRecord/Calculations/pluck
Daniel Rikowski
34

Wenn Sie auch zusätzliche Felder auswählen möchten:

Model.select('DISTINCT ON (models.ratings) models.ratings, models.id').map { |m| [m.id, m.ratings] }
Marcin Nowicki
quelle
1
select extra fields<3 <3
cappie013
27
Model.uniq.pluck(:rating)

# SELECT DISTINCT "models"."rating" FROM "models"

Dies hat den Vorteil, dass keine SQL-Zeichenfolgen verwendet und keine Modelle instanziiert werden

Cameron Martin
quelle
3
Dies wirft einen Fehler mit Rails 5.1 / AR 5.1 => undefinierte Methode `uniq '
Graham Slick
24
Model.select(:rating).uniq

Dieser Code funktioniert als 'DISTINCT' (nicht als Array # uniq), da Rails 3.2

Kuboon
quelle
5
Model.select(:rating).distinct
hassan_i
quelle
2
Dies ist die einzige offiziell korrekte Antwort, die auch sehr effizient ist. Durch Hinzufügen .pluck(:rating)am Ende wird jedoch genau das erreicht, was das OP verlangt hat.
Sheharyar
5

Wenn ich auf dem richtigen Weg bin, dann:

Aktuelle Abfrage

Model.select(:rating)

gibt ein Array von Objekten zurück und Sie haben eine Abfrage geschrieben

Model.select(:rating).uniq

uniq wird auf ein Array von Objekten angewendet und jedes Objekt hat eine eindeutige ID. uniq führt seine Aufgabe korrekt aus, da jedes Objekt im Array uniq ist.

Es gibt viele Möglichkeiten, eine bestimmte Bewertung auszuwählen:

Model.select('distinct rating').map(&:rating)

oder

Model.select('distinct rating').collect(&:rating)

oder

Model.select(:rating).map(&:rating).uniq

oder

Model.select(:name).collect(&:rating).uniq

Eine weitere Sache, erste und zweite Abfrage: Finden Sie unterschiedliche Daten durch SQL-Abfrage.

Diese Abfragen werden als "London" und "London" betrachtet. Dies bedeutet, dass der Platz vernachlässigt wird. Aus diesem Grund wird in Ihrem Abfrageergebnis einmal "London" ausgewählt.

Dritte und vierte Abfrage:

Finden Sie Daten durch SQL-Abfrage und für bestimmte Daten angewendet Ruby Uniq Mehtod. Diese Abfragen werden als "London" und "London" unterschiedlich betrachtet. Aus diesem Grund werden in Ihrem Abfrageergebnis "London" und "London" ausgewählt.

Bitte ziehen Sie zum besseren Verständnis das angehängte Bild vor und sehen Sie sich "Toured / Awaiting RFP" an.

Geben Sie hier die Bildbeschreibung ein

uma
quelle
6
map& collectSind Aliase für dieselbe Methode, müssen keine Beispiele für beide angegeben werden.
Adam Lassek
4

Einige Antworten berücksichtigen nicht, dass das OP eine Reihe von Werten wünscht

Andere Antworten funktionieren nicht gut, wenn Ihr Modell Tausende von Datensätzen enthält

Trotzdem denke ich, dass eine gute Antwort lautet:

    Model.uniq.select(:ratings).map(&:ratings)
    => "SELECT DISTINCT ratings FROM `models` " 

Da Sie zuerst ein Array von Modellen generieren (mit einer aufgrund der Auswahl verringerten Größe), extrahieren Sie dann das einzige Attribut, das diese ausgewählten Modelle haben (Bewertungen).

Fernando Fabreti
quelle
3

Wenn jemand mit Mongoid dasselbe sucht, dann ist das so

Model.distinct(:rating)
Vassilis
quelle
Dieser funktioniert jetzt nicht, er gibt jetzt Vielfache zurück.
EUPHORAY
kehrt nicht eindeutig zurück
dowi
2

Eine andere Möglichkeit, Uniq-Spalten mit SQL zu sammeln:

Model.group(:rating).pluck(:rating)
Slava Zharkov
quelle