Sql 在Rails中,如何根据关系中的项目数或关系的条件返回一组记录?

Sql 在Rails中,如何根据关系中的项目数或关系的条件返回一组记录?,sql,ruby-on-rails,activerecord,Sql,Ruby On Rails,Activerecord,我正在编写一个Rails应用程序,其中有两个模型:机器模型和机器更新模型。机器型号有许多机器更新。机器更新有一个日期/时间字段。我正在尝试检索具有以下条件的所有计算机记录: 机器型号在过去2周内没有机器更新,或 机器型号从未有过任何更新 目前,我正在使用命名范围完成#1: named_scope :needs_updates, :include => :machine_updates, :conditions => ['machine_

我正在编写一个Rails应用程序,其中有两个模型:机器模型和机器更新模型。机器型号有许多机器更新。机器更新有一个日期/时间字段。我正在尝试检索具有以下条件的所有计算机记录:

  • 机器型号在过去2周内没有机器更新,或
  • 机器型号从未有过任何更新
  • 目前,我正在使用命名范围完成#1:

    named_scope :needs_updates,
                :include => :machine_updates,
                :conditions => ['machine_updates.date < ?', UPDATE_THRESHOLD.days.ago]
    
    命名的\u范围:需要\u更新,
    :include=>:机器更新,
    :条件=>['machine_updates.date<?',UPDATE_THRESHOLD.days.ago]
    

    然而,这只会得到至少有一次更新的机器型号。我还想检索没有更新的机器型号。如何修改
    需要更新
    ,使其返回的项目也满足该标准?

    一种解决方案是引入计数器缓存:

    # add a machine_updates_count integer database column (with default 0)
    # and add this to your Machine model:
    counter_cache :machine_updates_count
    
    然后将
    或machine\u updates\u count=0
    添加到SQL条件中

    但是,您也可以通过使用左联接在没有计数器缓存的情况下解决此问题:

    named_scope :needs_updates,
      :select => "machines.*, MAX(machine_updates.date) as last_update",
      :joins  => "LEFT JOIN machine_updates ON machine_updates.machine_id = machines.id",
      :group  => "machines.id",
      :having => ["last_update IS NULL OR last_update < ?", lambda{ UPDATE_THRESHOLD.seconds.ago }]
    

    一种解决方案是引入计数器缓存:

    # add a machine_updates_count integer database column (with default 0)
    # and add this to your Machine model:
    counter_cache :machine_updates_count
    
    然后将
    或machine\u updates\u count=0
    添加到SQL条件中

    但是,您也可以通过使用左联接在没有计数器缓存的情况下解决此问题:

    named_scope :needs_updates,
      :select => "machines.*, MAX(machine_updates.date) as last_update",
      :joins  => "LEFT JOIN machine_updates ON machine_updates.machine_id = machines.id",
      :group  => "machines.id",
      :having => ["last_update IS NULL OR last_update < ?", lambda{ UPDATE_THRESHOLD.seconds.ago }]
    

    如果您可以在表格中进行更改,则可以使用关联的:touch方法

    例如,将
    datetime
    列添加到名为
    last\u Machine\u update
    的机器中。此后,在机器更新的“所属对象”中,添加
    :touch=>:last\u machine\u update
    。这将导致该字段在您上次添加或修改连接到该计算机的MachineUpdate时更新,从而不再需要命名范围


    否则我可能会像Alex建议的那样做。

    如果您可以在表中进行更改,那么您可以使用“属于”关联的:touch方法

    例如,将
    datetime
    列添加到名为
    last\u Machine\u update
    的机器中。此后,在机器更新的“所属对象”中,添加
    :touch=>:last\u machine\u update
    。这将导致该字段在您上次添加或修改连接到该计算机的MachineUpdate时更新,从而不再需要命名范围


    否则我可能会按照亚历克斯的建议去做。

    我也遇到了类似的问题。其实很简单:

    Machine.all(
      :include => :machine_updates,
      :conditions => "machine_updates.machine_id IS NULL OR machine_update.date < ?", UPDATE_THRESHOLD.days.ago])
    

    我也遇到了类似的问题。其实很简单:

    Machine.all(
      :include => :machine_updates,
      :conditions => "machine_updates.machine_id IS NULL OR machine_update.date < ?", UPDATE_THRESHOLD.days.ago])
    

    最初实施此更改时,我遇到以下数据库错误:
    列“last\u update”不存在
    。我拿出上次更新的
    内容,并用
    MAX(…)
    行替换它。然后,我得到了以下数据库错误:
    类型timestamp的输入语法无效:“--!ruby/object:Proc{}
    。出于好奇,我删除了lambda,这导致了另一个数据库错误:
    列”machines.machine\u name“必须出现在GROUP BY子句中或用于聚合函数
    。另外,作为旁注,lambda似乎不是必需的;根据日志,每当刷新页面时,通过SQL语句发送的时间戳都会相应地更新。如果没有它,事情将在开发环境中工作,但在生产环境中不会工作。至于错误:(1)您使用的是什么版本的Rails?(2) 您使用的是哪种数据库?(3) 您是否向命名范围添加了任何选项?我创建了一个小的模拟应用程序,该代码在Rails2.3.5和SQLite3上运行。我正在使用Rails2.3.5和PostgreSQL 8.1。没有添加任何选项,我只是复制了您拥有的。lambda会导致SQL语法错误(而不使用它似乎不会导致问题)。您需要在日期方面使用lambda。如果您没有,则在您第一次启动应用程序时,
    UPDATE\u THRESHOLD.days.ago
    将被评估,并将在您下次重新启动应用程序之前的同一日期生效。lambda将负责在请求传入时重新评估该条件。当我最初实现此更改时,我遇到以下数据库错误:
    列“last\u update”不存在
    。我拿出上次更新的
    内容,并用
    MAX(…)
    行替换它。然后,我得到了以下数据库错误:
    类型timestamp的输入语法无效:“--!ruby/object:Proc{}
    。出于好奇,我删除了lambda,这导致了另一个数据库错误:
    列”machines.machine\u name“必须出现在GROUP BY子句中或用于聚合函数
    。另外,作为旁注,lambda似乎不是必需的;根据日志,每当刷新页面时,通过SQL语句发送的时间戳都会相应地更新。如果没有它,事情将在开发环境中工作,但在生产环境中不会工作。至于错误:(1)您使用的是什么版本的Rails?(2) 您使用的是哪种数据库?(3) 您是否向命名范围添加了任何选项?我创建了一个小的模拟应用程序,该代码在Rails2.3.5和SQLite3上运行。我正在使用Rails2.3.5和PostgreSQL 8.1。没有添加任何选项,我只是复制了您拥有的。lambda会导致SQL语法错误(而不使用它似乎不会导致问题)。您需要在日期方面使用lambda。如果您没有,则在您第一次启动应用程序时,
    UPDATE\u THRESHOLD.days.ago
    将被评估,并将在您下次重新启动应用程序之前的同一日期生效。lambda将在请求到达时重新评估该条件。