Rails中将Newsletter::Author的复杂SQL查询改写为Arel或ActiveRecord
改造后的ActiveRecord实现
你可以直接将原scope替换为以下代码,逻辑和原原生SQL完全对齐,且返回ActiveRecord::Relation支持后续链式调用其他查询方法:
# app/models/newsletter/author.rb scope :without_submissions_for, lambda { |newsletter_id| # 子查询:筛选出有草稿、无提交内容的成员ID eligible_member_ids = GroupMember .joins(:stories) .where(newsletter_stories: { status: :draft }) .where.not( exists: Newsletter::Story .where(status: :submitted) .where("newsletter_stories.author_id = group_members.id") ) .distinct .select(:id) # 主查询:匹配对应Newsletter下未删除、符合成员条件的作者 where(discarded_at: nil) .where(newsletter_id: newsletter_id) .where(group_member_id: eligible_member_ids) }
纯Arel无手写SQL版本
如果你需要完全避免手写SQL片段,可以用Arel表构建查询条件,写法如下:
# app/models/newsletter/author.rb scope :without_submissions_for, lambda { |newsletter_id| group_members = GroupMember.arel_table stories = Newsletter::Story.arel_table # 构造「存在提交状态稿件」的Arel条件 has_submitted_story = stories .project(1) .where(stories[:author_id].eq(group_members[:id])) .where(stories[:status].eq(Newsletter::Story.statuses[:submitted])) .exists eligible_member_ids = GroupMember .joins(:stories) .where(newsletter_stories: { status: :draft }) .where.not(has_submitted_story) .distinct .select(:id) where(discarded_at: nil) .where(newsletter_id: newsletter_id) .where(group_member_id: eligible_member_ids) }
两种写法生成的SQL和你原实现的执行效率完全一致,且符合Rails ActiveRecord查询的通用规范,方便后续维护。
内容的提问来源于stack exchange,提问作者Michael Bester
相关产品推荐
相关产品推荐

