如何将此原始查询转换为活动记录查询接口或 arel?

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

我想找到至少有 1 个 variantvariant_type = 1 并且没有任何 variantvariant_type = 2.

所以我的查询是这样的:

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

此外,一个course有多个course_variants

我在 where 子句中使用原始查询,我想使用 Active record 接口或 Arel 对此进行改进,对此有任何解决方案吗?

谢谢!

使用输入更新预期输出

输入

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}]}

输出

course: {id=2, course_variants: [{variant_type: 1}, {variant_type: 3}]}

如果你有 Course 和 CourseVarint 模型之间的关联,那么你可以这样做:

Course.joins(:course_variants).where(course_variants: {variant_type: 1}).where.not(course_variants: {variant_type: 2})

你应该使用 joins 方法连接两个表 coursescourse_variants 然后在 where 方法中定义条件,像这样:

Course.joins("INNER JOIN course_variants on courses.id = course_variants.course_id")
      .where("course_variants.variant_type" => 1)

试试这个:

Course.where(id: CourseVariant.where(variant_type: 1).pluck(:course_id)).where.not(id: CourseVariant.where(variant_type: 2).pluck(:course_id))

希望对您有所帮助。 :)

嘿,你可以试试这种查找此类记录的优化方法,这里不需要两次触发子句:

您可以在 Rails pluck 中执行此操作以进行第二次查询:

 Course.joins(:course_variants).where(:course_variants => {:variant_type => 1}).where("courses.id not in (?)",CourseVariant.where(:variant_type => 2).pluck(:course_id))

您可以在内部查询中使用 in 查询,这比上面更快:

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

您可以通过在 mysql 的单个查询中使用 group 作为:

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

您可以稍微调整一下模型关联:

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

您可能需要将 'non_type_3_variants_courses' 替换为 ARel 在进行连接时生成的正确别名(我没有 Rails env atm)