Rails中的复杂SQL查询4

时间:2014-04-02 15:01:16

标签: sql ruby-on-rails-4

我有一个复杂的查询,我需要在我的Rails应用程序中的范围,并尝试了很多没有运气的东西。我通过find_by_sql使用原始SQL,但想知道是否有任何大师想要拍摄。为了清楚起见,我会稍微简化一下这个措辞,但问题应该准确说明。

我有用户。用户拥有许多记录。其中一个是标记为当前的(#is_current = true),其余的则不是。每个CoiRecord都有很多关系。关系具有活动时的值(active_when),它取四个值,[1..4]。

值1和2被认为是最近的。值3和4不是。

问题最终是在用户上有一个范围(has_recent_relationships和has_no_recent_relationships),用于过滤他们是否在当前记录上有最近的关系。 (Old Records与此无关。)我尝试在Relationship上创建一个最近和not_recent范围,然后在Record上构建范围,并结合检查is_current == 1.这是我失败的地方。我必须继续使用该应用程序,但别无选择,只能使用原始SQL并继续使用该应用程序,希望稍后再次访问。我把它放在User上,这是我真正需要的唯一上下文,并为其他对象的范围留出代码。

有效的SQL,正确查找具有最近关系的用户,如下所示。另一个只是在HAVING子句中使用“= 0”而不是“> 0”。

SELECT * FROM users WHERE `users`.`id` IN (
  SELECT 
    records.owner_id
  FROM `coi_records`
  LEFT OUTER JOIN `relationships` ON `relationships`.`record_id` = `records`.`id`
  WHERE `records`.`is_current` = 1
  HAVING (
    SELECT count(*)
    FROM relationships
    WHERE ((record_id = records.id) AND ((active_when = 1) OR (active_when = 2)))
  ) > 0
)

我的直觉告诉我这很复杂,我的建模可能会重新设计和简化,但单个对象非常简单,只是从两个对象获取这些特定数据变得复杂。

无论如何,我很感激任何想法。我不期待完整的解决方案,因为,ick。只是觉得你们中间的受虐狂可能会觉得这很有趣。

2 个答案:

答案 0 :(得分:0)

您是否尝试过直接使用Arel和this website

只需复制并粘贴您的查询即可:

User.select(Arel.star).where(
  User.arel_table[:id].in(
    Relationship.select(Arel.star.count).where(
      Arel::Nodes::Group.new(
        Relationship.arel_table[:record_id].eq(Record.arel_table[:id]).and(
          Relationship.arel_table[:active_when].eq(1).or(Relationship.arel_table[:active_when].eq(2))
        )
      )
    ).joins(
      CoiRecord.arel_table.join(Relationship.arel_table, Arel::Nodes::OuterJoin).on(
        Relationship.arel_table[:record_id].eq(Record.arel_table[:id])
      ).join_sources
    ).ast
  )
)

答案 1 :(得分:0)

我设法找到一种方法来创建我需要的返回ActiveRelationship对象的方法,这简化了许多其他代码。这就是我想出来的。这可能无法很好地扩展,但是这个应用程序可能不会以如此多的数据结束,这将是一个问题。

我创建了两个范围方法。第二个取决于第一个简化事情:

def self.has_recent_relationships
  joins(records_owned: :relationships)
  .merge(Record.current)
  .where("(active_when = 1) OR (active_when = 2)")
  .distinct
end

def self.has_no_recent_relationships
  users_with_recent_relationships = User.has_recent_relationships.pluck(:id)
  if users_with_recent_relationships.length == 0
    User.all
  else
    User.where("id not in (?)", users_with_recent_relationships.to_a)
  end
end

第一个通过加入Record找到具有最近关系的用户,与选择当前记录的范围合并(应该只有一个),并查找正确的active_when值。很容易。

第二种方法找到具有最近关系的用户(使用第一种方法。)如果没有,那么所有用户都在那些没有最近关系的用户中,并且我返回User.all(这将永远不会发生在野外,但理论上它可以。)否则我会使用SQL关键字NOT IN和数组返回那些确实有最近关系的人的逆。如果数组变大,这部分可能是非高效的,但我现在正在使用它。