问题
I want to find courses
which have at least 1 variant
with variant_type
= 1 and don't have any variant
with variant_type
= 2.
So my query like:
Course.where("id IN ( SELECT course_id
FROM course_variants
WHERE variant_type = 1
)
AND id NOT IN (
SELECT course_id
FROM course_variants
WHERE variant_type = 2
)")
Additionally, a course
has many course_variants
.
I'm using a raw query in where clause, I want to improve this using Active record interface or Arel, any solution for this?
Thank you!
Update with expected output with input
Input
course: {id=1, course_variants: [{variant_type: 1}, {variant_type: 2}]}
course: {id=2, course_variants: [{variant_type: 1}, {variant_type: 3}]}
course: {id=3, course_variants: [{variant_type: 2}, {variant_type: 3}]}
Output
course: {id=2, course_variants: [{variant_type: 1}, {variant_type: 3}]}
回答1:
You may tweak your model association a bit:
class Course < ActiveRecord::Base
has_many :type_1_variants, class_name: "CourseVariant", -> { where(variant_type: 1) }
has_many :non_type_3_variants, class_name: "CourseVariant", -> { where.not(variant_type: 3) }
end
Course.joins(:type_1_variants, :non_type_3_variants).group(:course_id).having('COUNT(course_variants.id) > 0').having('COUNT(non_type_3_variants_courses.id) > 0')
You may need to replace 'non_type_3_variants_courses' with the proper alias name that ARel generates when doing joins (I don't have a Rails env atm)
回答2:
You should use joins method to join two tables courses
and course_variants
then define condition in where method, something like this:
Course.joins("INNER JOIN course_variants on courses.id = course_variants.course_id")
.where("course_variants.variant_type" => 1)
回答3:
Try this:
Course.where(id: CourseVariant.where(variant_type: 1).pluck(:course_id)).where.not(id: CourseVariant.where(variant_type: 2).pluck(:course_id))
Hope this help. :)
回答4:
Hey you can try this way it is optimised way to find such records and here no need fire in clause two times:
You can do this in Rails pluck for second query as:
Course.joins(:course_variants).where(:course_variants => {:variant_type => 1}).where("courses.id not in (?)",CourseVariant.where(:variant_type => 2).pluck(:course_id))
You can use in query internally in single query which is faster than above:
Course.joins(:course_variants).where(:course_variants => {:variant_type => 1}).where("courses.id not in (select course_id from course_variants where variant_type = 2)")
You can do this by using group
in single query for mysql as:
Course.joins(:course_variants).group("course_variants.course_id").having("SUM(CASE WHEN variant_type = 1 THEN 1
WHEN variant_type = 2 THEN 2
ELSE 0
END) = 1")
回答5:
If you have an association betweeen Course and CourseVarint models, then you can do as follows:
Course.joins(:course_variants).where(course_variants: {variant_type: 1}).where.not(course_variants: {variant_type: 2})
来源:https://stackoverflow.com/questions/37106706/how-to-convert-this-raw-query-to-active-record-query-interface-or-arel