6

我喜欢 Rails 的部分原因是我讨厌 SQL——我认为它更像是一种汇编语言,应该使用 ActiveRecord 等更高级的工具来操作。然而,我似乎已经达到了这种方法的极限,而且我对 SQL 的理解不够深入。

我有一个包含很多子记录的复杂模型。我还有一组 30-40 个 named_scopes 来实现来自客户端的业务逻辑。这些范围有条件地链接在一起,这就是为什么我有这些joins_范围,所以连接不会被破坏。

我有几个不能正常工作,或者至少不是客户希望他们工作的方式。这是模型结构的粗略概念,有几个命名范围(示例并非全部需要)来说明我的方法并指出我的问题。 (请原谅任何语法错误)

class Man < ActiveRecord::Base
  has_many :wives

  named_scope :has_wife_named       lambda { |n| { :conditions => { :wives => {:name => n}}}}
  named_scope :has_young_wife_named lambda { |n| { :conditions => { :wives => {:name => n, :age => 0..30}}}}
  named_scope :has_yw_named_v2      lambda { |n| { :conditions => ["wives.name = ? AND wives.age <= 30", n]}}
  named_scope :joins_wives         :joins => :wives

  named_scope :has_red_cat          :conditions => { :cats => {:color => 'red'}}        
  named_scope :has_cat_of_color     lambda { |c| { :conditions => { :cats => {:color => c}}}}
  named_scope :has_7yo_cat          :conditions => { :cats => {:age => 7}}
  named_scope :has_cat_of_age       lambda { |a| { :conditions => { :cats => {:age => a}}}}
  named_scope :has_cat_older_than   lambda { |a| { :conditions => ["cats.age > ?", a] }}
  named_scope :has_cat_younger_than lambda { |a| { :conditions => ["cats.age < ?", a] }}
  named_scope :has_cat_fatter_than  lambda { |w| { :conditions => ["cats.weight > ?", w] } }
  named_scope :joins_wives_cats     :joins => {:wives => :cats}
end

class Wife < ActiveRecord::Base
  belongs_to :man
  has_many :cats
end

class Cat < ActiveRecord::Base
  belongs_to :wife
end
  1. 我可以找到妻子的猫是红猫和七岁的男人

    @men = Man.has_red_cat.has_7yo_cat.joins_wives_cats.scoped({:select => 'DISTINCT men'})
    

    我什至可以找到妻子的猫体重超过 20 磅且超过 6 岁的男人

    @men = Man.has_cat_fatter_than(20).has_cat_older_than(5).joins_wives_cats.scoped({:select => 'DISTINCT men'})
    

    但这不是我想要的。我想找到妻子中至少有一只红猫和一只七岁的猫的男人,它们不必是同一只猫,或者找到妻子中至少有一只超过给定重量的猫的男人,并且一只比给定年龄大的猫。
    (在随后的示例中,请假设存在适当的joins_DISTINCT

  2. 我可以找到有妻子叫以斯帖的男人

    @men = Man.has_wife_named('Esther')
    

    我什至可以找到有妻子的男人叫 Esther、Ruth 或 Ada(真甜!)

    @men = Man.has_wife_named(['Esther', 'Ruth', 'Ada'])
    

    但我想找到有妻子的男人叫 Esther AND Ruth AND Ada。

  3. 哈哈,开个玩笑,其实我需要这个:我能找到老婆30岁以下叫Esther的男人

    @men = Man.has_young_wife_named('Esther')
    

    寻找有年轻妻子的男人,名叫 Esther、Ruth 或 Ada

    @men = Man.has_young_wife_named(['Esther', 'Ruth', 'Ada'])
    

    但如上所述,我想找到有年轻妻子的男人,名叫 Esther AND Ruth AND Ada。幸运的是,在这种情况下,最小值是固定的,但最好也指定一个最小年龄。

  4. 有没有办法用哈希语法测试不等式,还是你总是必须恢复- 注意和:conditions => ["", n]之间的区别- 我更喜欢第一个,但范围只适用于有限值。如果您正在寻找一位老太太,我想您可以使用,但是当一位妻子年满 101 岁时,她会放弃搜索。(嗯。她会做饭吗?j/k)has_young_wife_namedhas_yw_named_v2a..100

  5. 有没有办法在范围内使用范围?:has_red_cat如果可以:has_cat_of_color以某种方式使用它,或者如果有某种方法可以使用其父项中子记录的范围,我会喜欢它,这样我就可以将与猫相关的范围放入Wife模型中。

我真的不想在不使用的情况下直接使用 SQL 执行此操作named_scope,除非还有其他更好的东西 - 对插件的建议和非常感谢的东西,或者指导我需要学习的那种 SQL。一位朋友建议 UNION 或子搜索可以在这里工作,但在 Rails 的上下文中似乎没有太多讨论。我对视图一无所知-它们有用吗?有没有一种让他们满意的方法来制作它们?

谢谢!

当我要去圣艾夫斯时,
我遇到了一个有七个妻子的男人
每个妻子有七个麻袋
每个麻袋有七只猫
每只猫有七个工具
包 工具包,猫,麻袋,妻子
有多少人要去圣艾夫斯?

4

3 回答 3

2

好吧,我用named_scopes 得到了很好的结果,如下所示:

named_scope :has_cat_older_than   lambda { |a| { :conditions => ["men.id in ( select man_id from wives where wives.id in ( select wife_id from cats where age > ? ) )", a] } }

named_scope :has_young_wife_named lambda { |n| { :conditions => ["men.id in ( select man_id from wives where name = ? and age < 30)", n] } }

我现在可以成功地做

Member.has_cat_older_than(6).has_young_wife_named('Miriam').has_young_wife_named('Vashti')

并得到我所期待的。这些作用域不需要使用连接,而且它们似乎与其他风格的连接配合得很好。

w00t!

评论引出了这是否是一种有效的方法,或者是否有更“rails-y”的方法。将另一个模型的范围作为 sql 子查询片段包含在内的某种方法可能很有用......

于 2009-06-20T13:18:01.813 回答
2

我使用construct_finder_sql 在另一个named_scope 中完成子选择。它可能不适合所有人,但使用它可以让我们干掉我们用于报告的几个 named_scopes。

Man.has_cat_older_than(6).send(:construct_finder_sql,{})

在您的脚本/控制台中尝试。

于 2009-07-23T21:32:33.960 回答
0

您使用了最原生的 Rails 解决方案。直接 SQL 将具有相同的性能,因此没有理由使用它。

于 2009-06-20T07:38:17.287 回答