wake-up-neo.com

Suchen Sie alle Datensätze, deren Anzahl einer Assoziation größer als Null ist

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 erhalten, die mindestens 1 freie Stelle haben. Ich habe so etwas ausprobiert:

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).

66
JPHorta

joins verwendet standardmäßig einen inneren Join, so dass bei Verwendung von Project.joins(:vacancies) nur Projekte zurückgegeben werden, denen eine Vakanz zugeordnet ist.

AKTUALISIEREN:

Wie von @mackskatz im Kommentar ohne group-Klausel ausgeführt, gibt der obige Code doppelte Projekte für Projekte mit mehr als einer offenen Stelle zurück. Verwenden Sie zum Entfernen der Duplikate

Project.joins(:vacancies).group('projects.id')
46
jvnill

1) Projekte mit mindestens 1 Vakanz zu erhalten:

Project.joins(:vacancies).group('projects.id')

2) Projekte mit mehr als 1 Vakanz zu erhalten:

Project.joins(:vacancies).group('projects.id').having('count(project_id) > 1')

3) Oder, wenn das Modell Vacancy den Zählercache setzt:

belongs_to :project, counter_cache: true

dann funktioniert auch das:

Project.where('vacancies_count > ?', 1)

Flexionsregel für vacancy muss möglicherweise manuell angegeben werden ?

126
Arta

Ja, vacancies ist kein Feld im Join. Ich glaube du willst:

Project.joins(:vacancies).group("projects.id").having("count(vacancies.id)>0")
26
Peter Alfvin
# None
Project.joins(:vacancies).group('projects.id').having('count(vacancies) = 0')
# Any
Project.joins(:vacancies).group('projects.id').having('count(vacancies) > 0')
# One
Project.joins(:vacancies).group('projects.id').having('count(vacancies) = 1')
# More than 1
Project.joins(:vacancies).group('projects.id').having('count(vacancies) > 1')
10
Dorian

In Rails 4+ können Sie auch includes oder eager_load verwenden, um dieselbe Antwort zu erhalten:

Project.includes(:vacancies).references(:vacancies).
        where.not(vacancies: {id: nil})

Project.eager_load(:vacancies).where.not(vacancies: {id: nil})
1
konyak

Das Ausführen eines Inner-Joins für die Tabelle has_many in Kombination mit einer group oder uniq ist möglicherweise sehr ineffizient. In SQL wäre dies besser als Semi-Join zu implementieren, der EXISTS mit einer korrelierten Unterabfrage verwendet.

Auf diese Weise kann das Abfrageoptimierungsprogramm die Tabelle "Vakanzen" durchsuchen, um zu überprüfen, ob eine Zeile mit der richtigen project_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:

Project.where(Vacancies.where("vacancies.project_id = projects.id").exists)

Ebenso finden Sie alle Projekte, die keine offenen Stellen haben:

Project.where.not(Vacancies.where("vacancies.project_id = projects.id").exists)
1
David Aldridge

Ohne viel Rails-Zauber können Sie Folgendes tun:

Project.where('(SELECT COUNT(*) FROM vacancies WHERE vacancies.project_id = projects.id) > 0')

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 funktioniert die Verkettung der .count-Methode gut. Ich wurde vorher durch Abfragen wie Project.joins(:vacancies) gebrannt. Natürlich gibt es Vor- und Nachteile, da es keine DB-Agnostiker sind.

0
konyak

Ich denke, es gibt eine einfachere Lösung:

Project.joins(:vacancies).uniq
0
Yuri Karpovich