对 Rails 4 的 SQL 查询

问题描述

我很难在 Rails 中转换这个查询。我有两个模型 CategoryProduct。每个类别都有许多产品。我们可以通过 product.category_id 获取特定产品的类别。有人可以帮我将此查询转换为 rails 4 吗?谢谢。


select * from category
where not exists 
(
 select from product
 where product.category_id = category.id 
 and product.validity_time >= now() - interval '1 month'
)

解决方法

Rails 便捷查询方法非常适用于许多用例,但它们并没有提供任何给定数据库可以做的全部范围的暴露。 Rails 团队不断改进此顶级 DSL,以便为更常用的功能提供更好的可见性(例如,Rails 5 实现了 where.not,Rails 6 添加了 where.missing)。

也就是说,对于更复杂的查询,您需要比 ActiveRecord::QueryMethods 提供的便捷方法更深入地研究才能实现您的目标。

要实现这种类型的查询,您将不得不使用 String 或使用 Arel(rails 的底层查询汇编程序)。我不喜欢 Rails 中的 String SQL,所以我将在这里提供 Arel 解决方案。

如果您想要确切的 SQL:

product_table = Product.arel_table
category_table = Category.arel_table
Category.where(
  Arel::Nodes::Not.new(
    Arel::Nodes::Exists.new(
      product_table.project(Arel.star).where(
        product_table[:category_id].eq(category_table[:id]).and(
          product_table[:validity_time].gteq(
            Arel::Nodes::Subtraction.new(
              Arel::Nodes::SqlLiteral.new('now()'),Arel::Nodes::SqlLiteral.new("interval '1 month'")
        )))
      )))
)

其他选项:这些选项应该产生相同的结果(使用不同的 SQL),并且实现和组装更简单、更容易理解。

  1. 不在 () 的地方
product_table = Product.arel_table
category_table = Category.arel_table
Category.where(
  category_table[:id].not_in(
    product_table.project(product_table[:category_id])
      .where(
          product_table[:validity_time].gteq(Time.now - 1.month)
      )
  )
)

生成的 SQL 应该类似于:

SELECT categories.*
FROM categories
WHERE 
  categories.id NOT IN ( 
     SELECT products.category_id
     FROM products
     WHERE 
       products.validity_time >= [RUN TIME - 1 MONTH]
  )
  1. 多条件加入
product_table = Product.arel_table
category_table = Category.arel_table
join = category_table.join(product_table).on(
  products_table[:category_id].eq(category_table[:id]).and(
    product_table[:validity_time].gteq(
      Arel::Nodes::Subtraction.new(Arel::Nodes::SqlLiteral.new('now()'),Arel::Nodes::SqlLiteral.new("interval '1 month'")))))
Category.joins(join.join_sources).where(products: {id: nil }).distinct

SQL

SELECT DISTINCT categories.*
FROM 
  categories
  LEFT OUTER JOIN products ON products.category_id = categories.id
    AND products.validity_time >= now() - interval '1 month'
WHERE 
  products.id IS NULL

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...