我有一个复杂的查询,我需要在我的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。只是觉得你们中间的受虐狂可能会觉得这很有趣。
答案 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和数组返回那些确实有最近关系的人的逆。如果数组变大,这部分可能是非高效的,但我现在正在使用它。