Rails关联表日期计算查询报错,如何无需原生SQL解决?
问题分析与解决
错误根源
你在Arel表达式里直接把Ruby的Date.today对象和数据库字段days_to_keep做减法,属于Ruby代码与SQL表达式的非法混合——数据库无法解析这种跨环境的运算逻辑,所以抛出TypeError(expected numeric)。
不用原生SQL的正确实现
要让运算在数据库层面执行,需用Arel将Date.today转化为数据库能识别的表达式,再和字段做计算,以下是两种可行写法:
写法一:用Arel节点构建运算
# 注意:Rails约定模型名应为单数Event,原代码的Events不符合规范,建议修正 class Event < ApplicationRecord belongs_to :parent_event, class_name: "Event" # 若关联自身,建议用别名区分 scope :scheduled_clean, -> { joins(:parent_event) .where( arel_table[:created_at].lt( Arel::Nodes::Subtraction.new( Arel::Nodes::NamedFunction.new('CURRENT_DATE', []), ParentEvent.arel_table[:days_to_keep] ) ) ) } end
- 用
CURRENT_DATESQL函数代替Ruby的Date.today,让数据库使用自身的当前日期值 - 用
Arel::Nodes::Subtraction.new构建数据库层面的减法,确保两个操作数都是SQL表达式
写法二:简化的Arel链式写法
class Event < ApplicationRecord belongs_to :parent_event, class_name: "Event" scope :scheduled_clean, -> { joins(:parent_event) .where( arel_table[:created_at].lt( Arel.sql('CURRENT_DATE') - ParentEvent.arel_table[:days_to_keep] ) ) } end
Arel.sql('CURRENT_DATE')将SQL函数转为Arel节点,之后可直接用-运算符和字段节点运算,Arel会自动生成正确的SQL减法语句。
特殊场景:用应用服务器时间而非数据库时间
如果需要统一使用应用服务器的Date.today而非数据库时间,可将Ruby日期转为SQL字面量:
class Event < ApplicationRecord belongs_to :parent_event, class_name: "Event" scope :scheduled_clean, -> { today = Date.today joins(:parent_event) .where( arel_table[:created_at].lt( Arel::Nodes::Subtraction.new( Arel::Nodes.build_quoted(today), ParentEvent.arel_table[:days_to_keep] ) ) ) } end
Arel::Nodes.build_quoted(today)会把Ruby的Date对象转为数据库可识别的日期字面量(如'2024-05-20'),再与字段做减法。
内容的提问来源于stack exchange,提问作者willyab
相关产品推荐
相关产品推荐

