You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将目标SQL转为ActiveRecord scope?修复日期筛选异常

问题

如何将以下SQL转换为ActiveRecord scope?

SELECT * FROM trxes 
WHERE date="2022-05-01" 
AND id IN (SELECT lines.trx_id FROM lines WHERE lines.category_id = 15) 
OR id IN (SELECT id FROM trxes WHERE trxes.category_id = 15)

当前使用的scope如下,但会返回2022年5月以外的Trx记录:

scope :category_id, -> (cat_id) { where(lines: Line.where(category_id: cat_id)).or(Trx.where(category_id: cat_id)) }

执行Trx.date("2022-May-01").category_id(15)后生成的SQL显示,日期条件仅作用于OR的前半部分,导致后半部分Trx不受日期限制。

补充信息:简单Trx拥有Fuel、Groceries等分类,无Line对象;复杂Trx分类为Split,包含多个带分类的Line对象。需求是查询分类为Fuel的Trx,即自身分类为Fuel的简单Trx,以及包含分类为Fuel的Line的复杂Trx。

模型关联:

class Trx < ApplicationRecord
  belongs_to :category, optional: true
  has_many :lines, dependent: :destroy
...
class Line < ApplicationRecord
  belongs_to :trx
  belongs_to :category, optional: true
...
class Category < ApplicationRecord
  has_many :lines
  has_many :trxes
...

期望合并以下两个ActiveRecord查询的结果:

Trx.where(date:"2022-May-01").where(category_id: 15)
Trx.where(date:"2022-May-01").where(lines: Line.where(category_id: 15))
解决方案

问题出在当前scope的逻辑上——or方法直接拼接前后两个条件,导致日期条件仅绑定到where(lines: ...)分支,而Trx.where(category_id: cat_id)不受外层日期约束。要让日期条件作用于整个OR逻辑的两边,可采用以下几种方法:

方法1:正确嵌套OR条件

修改scope,用self.where代替直接调用Trx.where,让OR的两个分支继承当前查询上下文(包括日期条件):

scope :category_id, -> (cat_id) {
  where(lines: Line.where(category_id: cat_id))
    .or(self.where(category_id: cat_id))
}

调用Trx.date("2022-May-01").category_id(15)时,日期条件会自动应用到OR的两边,生成的SQL如下:

SELECT "trxes".* FROM "trxes" 
WHERE "trxes"."date" = '2022-05-01' 
AND (
  EXISTS (SELECT 1 FROM "lines" WHERE "lines"."trx_id" = "trxes"."id" AND "lines"."category_id" = 15) 
  OR "trxes"."category_id" = 15
)

方法2:用子查询构建IN条件

贴近原始SQL的写法,用子查询生成IN条件,同时保证日期条件作用于整个查询:

scope :category_id, -> (cat_id) {
  where(
    id: Line.where(category_id: cat_id).select(:trx_id)
  ).or(
    where(category_id: cat_id)
  )
}

方法3:使用UNION合并查询(Rails 6+支持)

直接合并你期望的两个查询结果,自动带上外层日期条件:

scope :category_id, -> (cat_id) {
  query1 = where(category_id: cat_id)
  query2 = where(lines: Line.where(category_id: cat_id))
  query1.union(query2)
}

效果验证

无论采用哪种方法,调用Trx.date("2022-May-01").category_id(15)后,都会同时筛选出两类符合条件的Trx:

  • 自身分类为15的简单Trx
  • 包含分类为15的Line的复杂Trx

内容的提问来源于stack exchange,提问作者chug

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 03:40:22