【问题标题】:How to make query conditional on associated table in ActiveRecord如何在 ActiveRecord 中以关联表为条件进行查询
【发布时间】:2019-05-14 00:11:00
【问题描述】:

我正在尝试创建一个查询,该查询将根据关联表中的条件从一个表中返回不同的记录。

具体来说,有一个Act 模型,它有_many Events,当然还有“事件”属于Act 我想找到那些在特定日期没有EventActs

查询需要使用not 条件运行,否则结果将不会排除任何Act 记录。

我尝试了多种构造方法,包括跨类方法使用合并,但没有奏效。

#Act model
def self.busy_on(date)
    joins(:event).merge.not(Event.busy_on(date))
end
#Event model
def self.busy_on(date)
    where(date: date)
end
#Controller
Act.busy_on(params[:date])

上面给了我一个参数错误(期望 1 得到 0),但我不确定它是否正确。

【问题讨论】:

  • 你可以使用 join & id is null 的组合,比如joins(:event).where(events: { date: data, id: nil })

标签: ruby-on-rails activerecord


【解决方案1】:

不确定这是最优雅或“正确”的方法,但这是我唯一能够做到的:

#Event model
def self.act_busy_on(date)
    where(date: date).pluck(:act_id)
  end
#Act model
def self.available_on(date)
    where.not(id: Event.act_busy_on(date))
  end
#Controller
Act.available_on(params[:date])

【讨论】:

    【解决方案2】:

    这是我建议的解决方法。

    我们将为忙碌和可用创建倒置范围,像这样

    class Act < ApplicationRecord
       has_many :events 
    
       scope :busy_on, ->(date) { joins(:events).where(events: {date: date}) }
       scope :available_on, ->(date) {where.not(id: busy_on(date).select(:id))}
    end 
    

    在这里,我们为Act 忙碌的日子创建一个范围,然后我们使用该范围作为计数器过滤器来确定该行为是否可用。 busy_on 范围的生成 SQL 将是:

      SELECT 
         acts.* 
      FROM 
         acts
         INNER JOIN events ON acts.id = events.act_id
      WHERE 
         events.date = [THE DATE YOU PASS INTO THE SCOPE]
    

    因此,available_on 范围的结果 SQL 将是:

     SELECT 
       acts.* 
     FROM 
       acts 
     WHERE 
       acts.id NOT IN ( 
          SELECT 
             acts.id
          FROM 
             acts
             INNER JOIN events ON acts.id = events.act_id
          WHERE 
             events.date = [THE DATE YOU PASS INTO THE SCOPE]
       ) 
    

    【讨论】:

    • 太棒了!一个非常优雅的解决方案。
    【解决方案3】:

    首先需要在joins查询中将event修改为events

    您要求Acts 在您的问题中的特定日期免费,因此查询类似于,

    def get_acts(date)
        includes(:events).where('events.date = ? and events.id = ?', date, nil)
    end
    

    现在,如果您致电Act.get_acts(26-05-2019),将在当天为您提供免费活动。

    【讨论】:

      【解决方案4】:
      class Act < ApplicationRecord
        def self.busy_on(date)
          #includes(:events).where(events: {created_at: date, id: nil })
         includes(:events).where("events.created_at = ? AND events.id = ?", date, nil)
        end
      end
      

      【讨论】:

      • 这似乎应该有效。生成的 SQL 是 'SELECT "acts".* FROM "acts" INNER JOIN "events" ON "events"."act_id" = "acts"."id" WHERE (events.date = '2019-05-14' AND events.id = NULL)' 但结果仍然是一个空数组。
      • joins 是一个INNER JOIN,所以没有id就没有事件
      • 现在使用includes,您需要添加references(:events),否则查询中将不会出现事件表,从而导致 SQL 错误。
      • @engineersmnky 感谢您的指正,我可以看到已经存在has_many :events 关联,所以还需要references(:events) 吗?
      • @Gabbar 是的,因为带有字符串条件的includes 不会离开加入事件表。 includes 只会在 2 个条件下离开联接 1) 如果您明确添加 references(:association_name) 2) 如果您使用引用关联表的 Hash where 条件(如您的注释代码中)。否则,include 的作用是在第一个查询中加载主表中的所有记录,然后触发第二个查询以根据第一个查询中的 id 加载关联
      猜你喜欢
      • 2017-08-05
      • 2011-10-25
      • 1970-01-01
      • 1970-01-01
      • 2018-02-21
      • 1970-01-01
      • 2011-07-28
      • 2021-11-07
      • 2015-02-08
      相关资源
      最近更新 更多