【问题标题】:Complex rails find ordering复杂的铁轨找到订购
【发布时间】:2010-08-03 10:43:00
【问题描述】:

我正在尝试查找哪些订单按他们的家名,然后按客户的姓氏。

Customer.find(:all, 
    :conditions =>['customers.id IN (?)', intersection], 
    :joins => 'JOIN histories ON histories.customer_id = customers.id
     JOIN houses ON histories.house_id = houses.id',
    :order => "houses.name ASC, customers.last_name ASC",
    :select => "customers.*, histories.job_title, houses.name"
)

我的问题是这将返回与每个客户相关的所有历史记录。

如果我添加 AND histories.finish_date IS NULL 这将阻止所选客户的所有历史记录被退回,但也会阻止交叉路口中没有历史记录或完成日期设置的客户被退回。

基本上,我需要交叉路口的每个客户返回一次,并带有当前的房屋名称(如果他们有的话),然后按他们的房屋名称和姓氏排序。

那么有没有办法做到这一点?

这是一个例子

客户

id  last_name
1     franks
2     doors
3     greens

历史

id    finish_date      house_id     customer_id
1        NULL             1             1
2        NULL             2             2
3        11/03/10         2             1
4        22/04/09         1             2

NULL = 当前房子

房屋

id name
1    a
2    b

结果

交叉点 = 1,2,3

last_name       house     
franks            a
doors             b
greens            NULL

谢谢

【问题讨论】:

    标签: sql ruby-on-rails activerecord


    【解决方案1】:

    我认为您需要使用外连接。

    例如,这应该可以工作:

    Customer.find(:all, 
      :conditions =>['customers.id IN (?) and histories.finish_date is null', intersection], 
      :joins => 'LEFT OUTER JOIN histories ON histories.customer_id = customers.id
        LEFT OUTER JOIN houses ON histories.house_id = houses.id',
      :order => "houses.name ASC, customers.last_name ASC",
      :select => "customers.*, histories.job_title, houses.name"
    )
    

    如果您在 Customer 和 History 之间以及 History 和 House 之间建立了关联,您应该可以使用 :include => [:histories => :house] 而不是 :joins 选项。

    唯一的另一件事是没有房子的客户将首先出现在列表中,因为 NULL 在订单中比非 NULL 值更早。您可能想尝试这样的订单选项:

    :order => 'isnull(houses.name), houses.name, customers.last_name'
    

    实现您指定的目标。

    【讨论】:

    • 查询只返回有历史的人而不是没有历史的人。是否存在即使您没有历史记录仍会返回客户的加入?
    • 很好奇。这在本地对我有用。外部联接应该返回客户,即使他们没有历史记录。您能否查看您的日志并查看从该查找中生成了哪些 SQL 查询?
    • 谢谢!刚刚将查询更改为使用 :include 现在它可以工作了
    • 看来我们是同时发的。 join 和 include 有什么区别?
    • 使用关联, :include 将执行外部连接, :joins 将执行内部连接。因此,使用 :include 您将让客户返回,无论他们是否有历史记录。使用 :join 您只会获得至少有一个历史记录的客户。
    【解决方案2】:

    IMO 在 Rails 中而不是在数据库中执行排序逻辑更简单:

    customers = Customer.find(:all, :conditions => { :id  => intersection }, :include => [ { :histories => :houses } ])
    
    customers.sort_by { |c| c.last_name }
    customers.sort_by do |c|
      current_house = c.histories.find_by_finish_date(nil) # Returns nil if no matching record found
      if current_house
        current_house.name
      else
        ''
      end
    end
    

    说明

    • :conditions 可以采用哈希 { :column_name => array } 转换为您的 IN where-condition
    • :如果存在相应的关联,则包括预加载 (eager loading) 表。换句话说::joins 创建 INNER JOIN,而 :include 创建LEFT JOINs。在这里,我们将离开连接历史并再次离开连接房屋。您可以省略这个 :include 标记,在这种情况下,rails 会在您每次访问历史记录或房屋属性时执行新查询。
    • sort_by 允许定义自定义排序标准。
    • find_by_finish_date 是 rails 的神奇方法之一;相当于h.find(:conditions => {:finish_date => nil })
    • 如何输出:只需将它们全部输出到您的视图中即可。如果他没有历史记录,customer.histories 是一个空数组。

    【讨论】:

    • 感谢您的回复。我在我的问题中添加了一个示例。我遇到的问题希望更清楚:)
    • 我明白你的意思,但我遇到的问题是,即使他们没有历史记录,你如何找到返回客户详细信息的方法?
    猜你喜欢
    • 2012-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 2011-12-06
    • 2016-09-25
    相关资源
    最近更新 更多