Saya mencoba melakukan sesuatu yang menurut saya sederhana, tetapi ternyata tidak.
Saya memiliki model proyek yang memiliki banyak lowongan.
class Project < ActiveRecord::Base
has_many :vacancies, :dependent => :destroy
end
Saya ingin mendapatkan semua proyek yang memiliki minimal 1 lowongan. Saya mencoba sesuatu seperti ini:
Project.joins(:vacancies).where('count(vacancies) > 0')
tapi tertulis
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)
.
sumber
Project.joins(:vacancies).distinct
?1) Untuk mendapatkan Proyek dengan setidaknya 1 lowongan:
2) Untuk mendapatkan Proyek dengan lebih dari 1 lowongan:
3) Atau, jika
Vacancy
model menyetel cache penghitung:maka ini juga akan berhasil:
Aturan inflasi untuk
vacancy
mungkin perlu ditentukan secara manual ?sumber
Project.joins(:vacancies).group('projects.id').having('count(vacancies.id) > 1')
? Menanyakan jumlah lowongan alih-alih id proyekprojects.id
,project_id
, danvacancies.id
. Saya memilih untuk menghitungproject_id
karena ini adalah bidang tempat gabungan dibuat; tulang belakang bergabung jika Anda mau. Itu juga mengingatkan saya bahwa ini adalah tabel gabungan.Ya,
vacancies
bukan bidang yang di gabung. Saya yakin Anda ingin:sumber
sumber
Melakukan gabungan dalam ke tabel has_many yang dikombinasikan dengan
group
atauuniq
berpotensi sangat tidak efisien, dan dalam SQL ini akan lebih baik diimplementasikan sebagai gabungan semi yang menggunakanEXISTS
dengan subkueri berkorelasi.Ini memungkinkan pengoptimal kueri untuk menyelidiki tabel lowongan untuk memeriksa keberadaan baris dengan project_id yang benar. Tidak masalah apakah ada satu baris atau sejuta yang memiliki project_id tersebut.
Itu tidak semudah di Rails, tetapi bisa dicapai dengan:
Demikian pula, temukan semua proyek yang tidak memiliki lowongan:
Sunting: dalam versi Rails terbaru Anda mendapatkan peringatan penghentian yang memberitahu Anda untuk tidak mengandalkan
exists
pendelegasian ke arel. Perbaiki ini dengan:Sunting: jika Anda tidak nyaman dengan SQL mentah, coba:
Anda dapat mengurangi kekacauan ini dengan menambahkan metode kelas untuk menyembunyikan penggunaannya
arel_table
, misalnya:... jadi ...
sumber
Vacancy.where("vacancies.project_id = projects.id").exists?
menghasilkantrue
ataufalse
.Project.where(true)
adalah sebuahArgumentError
.Vacancy.where("vacancies.project_id = projects.id").exists?
tidak akan dieksekusi - ini akan menimbulkan kesalahan karenaprojects
relasinya tidak akan ada dalam kueri (dan juga tidak ada tanda tanya dalam kode contoh di atas). Jadi menguraikan ini menjadi dua ekspresi tidak valid dan tidak berfungsi. Baru-baru ini, RailsProject.where(Vacancies.where("vacancies.project_id = projects.id").exists)
memunculkan peringatan penghentian ... Saya akan memperbarui pertanyaannya.Di Rails 4+, Anda juga dapat menggunakan include atau eager_load untuk mendapatkan jawaban yang sama:
sumber
Saya pikir ada solusi yang lebih sederhana:
sumber
Tanpa banyak keajaiban Rails, Anda dapat melakukan:
Jenis kondisi ini akan bekerja di semua versi Rails karena sebagian besar pekerjaan dilakukan langsung di sisi DB. Plus,
.count
metode rantai juga akan bekerja dengan baik. Saya telah dibakar oleh pertanyaan sepertiProject.joins(:vacancies)
sebelumnya. Tentu saja ada pro dan kontra karena ini bukan DB agnostik.sumber
Anda juga dapat menggunakan
EXISTS
denganSELECT 1
daripada memilih semua kolom darivacancies
tabel:sumber
Kesalahannya memberi tahu Anda bahwa lowongan bukanlah kolom dalam proyek, pada dasarnya.
Ini seharusnya berhasil
sumber
aggregate functions are not allowed in WHERE