Ich versuche etwas zu tun, von dem ich dachte, es wäre einfach, aber es scheint nicht so zu sein.
Ich habe ein Projektmodell mit vielen offenen Stellen.
class Project < ActiveRecord::Base
has_many :vacancies, :dependent => :destroy
end
Ich möchte alle Projekte bekommen, die mindestens 1 freie Stelle haben. Ich habe so etwas versucht:
Project.joins(:vacancies).where('count(vacancies) > 0')
aber es heißt
SQLite3::SQLException: no such column: vacancies: SELECT "projects".* FROM "projects" INNER JOIN "vacancies" ON "vacancies"."project_id" = "projects"."id" WHERE ("projects"."deleted_at" IS NULL) AND (count(vacancies) > 0)
.
Project.joins(:vacancies).distinct
?1) Um Projekte mit mindestens 1 offenen Stelle zu erhalten:
2) Um Projekte mit mehr als einer offenen Stelle zu erhalten:
3) Oder wenn das
Vacancy
Modell den Zähler-Cache setzt:dann funktioniert das auch:
Die Flexionsregel für muss
vacancy
möglicherweise manuell angegeben werden .quelle
Project.joins(:vacancies).group('projects.id').having('count(vacancies.id) > 1')
? Abfrage der Anzahl der offenen Stellen anstelle der Projekt-IDsprojects.id
,project_id
undvacancies.id
. Ich habe mich für das Zählen entschieden,project_id
weil es das Feld ist, auf dem die Verknüpfung hergestellt wird. die Wirbelsäule der Verbindung, wenn Sie so wollen. Es erinnert mich auch daran, dass dies eine Join-Tabelle ist.Ja,
vacancies
ist kein Feld im Join. Ich glaube du willst:quelle
quelle
Das Durchführen eines inneren Joins für die Tabelle has_many in Kombination mit einem
group
oderuniq
ist möglicherweise sehr ineffizient. In SQL ist dies besser als Semi-Join implementiert, derEXISTS
mit einer korrelierten Unterabfrage verwendet wird.Auf diese Weise kann der Abfrageoptimierer die Tabelle der offenen Stellen prüfen, um festzustellen, ob eine Zeile mit der richtigen Projekt-ID vorhanden ist. Es spielt keine Rolle, ob es eine Zeile oder eine Million gibt, die diese project_id haben.
Das ist in Rails nicht so einfach, kann aber erreicht werden mit:
Ebenso finden Sie alle Projekte, die keine offenen Stellen haben:
Bearbeiten: In neueren Rails-Versionen wird eine Warnung angezeigt, dass Sie sich nicht darauf verlassen sollen
exists
, an arel delegiert zu werden. Beheben Sie dies mit:Bearbeiten: Wenn Sie mit Raw SQL nicht vertraut sind, versuchen Sie:
Sie können dies weniger chaotisch machen, indem Sie Klassenmethoden hinzufügen, um die Verwendung von
arel_table
beispielsweise zu verbergen :... so ...
quelle
Vacancy.where("vacancies.project_id = projects.id").exists?
ergibt entwedertrue
oderfalse
.Project.where(true)
ist einArgumentError
.Vacancy.where("vacancies.project_id = projects.id").exists?
wird nicht ausgeführt - es wird ein Fehler ausgelöst, da dieprojects
Beziehung in der Abfrage nicht vorhanden ist (und der obige Beispielcode auch kein Fragezeichen enthält). Das Zerlegen in zwei Ausdrücke ist also nicht gültig und funktioniert nicht. In den letzten Rails wirdProject.where(Vacancies.where("vacancies.project_id = projects.id").exists)
eine Abwertungswarnung ausgegeben ... Ich werde die Frage aktualisieren.In Rails 4+, können Sie dort auch enthalten oder eager_load die gleiche Antwort zu bekommen:
quelle
Ich denke, es gibt eine einfachere Lösung:
quelle
Ohne viel Rails-Magie können Sie Folgendes tun:
Diese Art von Bedingungen funktioniert in allen Rails-Versionen, da ein Großteil der Arbeit direkt auf der DB-Seite ausgeführt wird. Außerdem
.count
funktioniert die Verkettungsmethode auch gut. Ich bin wieProject.joins(:vacancies)
zuvor von Fragen verbrannt worden . Natürlich gibt es Vor- und Nachteile, da es nicht DB-Agnostiker ist.quelle
Sie können auch verwenden ,
EXISTS
mitSELECT 1
eher als alle Spalten aus dervacancies
Auswahltabelle:quelle
Der Fehler sagt Ihnen, dass offene Stellen im Grunde genommen keine Spalte in Projekten sind.
Das sollte funktionieren
quelle
aggregate functions are not allowed in WHERE