SQL 查询到 Rails 4
SQL query to Rails 4
我很难在 rails 中转换此查询。我有两个模型 Category
和 Product
。每个类别都有很多产品。我们可以通过 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 的基础查询汇编程序)。我不喜欢 String
SQL in rails 所以我会在这里提供 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)并且实现和组装更简单,更容易理解。
- 哪里不在 ()
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]
)
- 多条件加入
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
我很难在 rails 中转换此查询。我有两个模型 Category
和 Product
。每个类别都有很多产品。我们可以通过 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 的基础查询汇编程序)。我不喜欢 String
SQL in rails 所以我会在这里提供 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)并且实现和组装更简单,更容易理解。
- 哪里不在 ()
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]
)
- 多条件加入
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