How to convert this raw query to Active record query interface or arel?

别等时光非礼了梦想. 提交于 2019-12-22 00:13:15

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!