【问题标题】:how to modify complex sql query w/ join into rails 3如何使用/加入rails 3修改复杂的sql查询
【发布时间】:2010-10-08 09:39:25
【问题描述】:

我正在开发一个拼车应用程序,用户可以在其中添加电梯,并能够为每个电梯选择多个停靠点(A 到 B,通过 c、d、e)。现在,当用户在数据库中搜索电梯时,结果还应包括“A 到 d”、“c 到 B”或“c 到 e”等电梯。

我使用下面的代码与 Rails 2.3.5 一起工作,但很难将其移至 Rails 3。我确信必须有一种更清洁的方法来实现我想要做的事情并将一些代码移到模型中。

如果有人能帮我解决这个问题,那就太好了。

class Lift < ActiveRecord::Base
  has_many :stops
end

class Stop < ActiveRecord::Base           
  belongs_to :lift
  belongs_to :city  
end

class City < ActiveRecord::Base
  has_one :stop
end


@lifts = Lift.find(
  :select =>     "lifts.id, bs.city_id as start_city_id, bs2.city_id as destination_city_id",
  :from =>       "lifts",
  :joins =>      "LEFT JOIN stops bs ON lifts.id = bs.lift_id
                  LEFT JOIN stops bs2 ON lifts.id = bs2.lift_id
                  JOIN cities bc ON bs.city_id = bc.id
                  JOIN cities bc2 ON bs2.city_id = bc2.id",
  :include =>    [:stops, :cities],
  :conditions => "bs.lift_id = bs2.lift_id AND bs.position < bs2.position"
                  #uses the position attribute to determine the order of the stops
)

【问题讨论】:

    标签: ruby-on-rails activerecord mysql ruby-on-rails-3


    【解决方案1】:

    我知道这是一个非常简单的答案,但为什么不用这些选项在模型中创建一个范围呢?

    【讨论】:

    • 我也想过这个问题,但我不知道如何正确处理连接。也许你可以给我一些建议。
    【解决方案2】:

    我有点困惑你同时使用 find 的 :select 和 :include 选项。我知道当你使用 :include 时 :select 会被忽略(但认为有一个插件可以解决这个问题)。此外,您还包括 :cities,这种关系不会出现在您的代码中。

    无论如何,如果您想为 Lift 模型添加一些范围,我会考虑切换到 Arel API,这是 Rails 3 中的首选方法。http://m.onkey.org/2010/1/22/active-record-query-interface

    您可以将整个查询填充到一个范围内(不要认为您需要 :from 选项 - 可能是错误的):

    class Lift
    ...
      scope :with_all_stops, select('lifts.id, bs.city_id as start_city_id, bs2.city_id as destination_city_id').\
                             joins('LEFT JOIN stops bs ON lifts.id = bs.lift_id ' +
                                   'LEFT JOIN stops bs2 ON lifts.id = bs2.lift_id ' +
                                   'JOIN cities bc ON bs.city_id = bc.id ' +
                                   'JOIN cities bc2 ON bs2.city_id = bc2.id').\
                             includes(:stops, :cities).\
                             where('bs.lift_id = bs2.lift_id AND bs.position < bs2.position')
    ...
    end
    

    然后只需调用 Lift.with_all_stops。或者您可以根据自己的条件进行链接:Lift.with_all_stops.where('cities.name="Topeka"')。真的很强大。

    如果该查询的某些部分本身有用(在这种特殊情况下值得怀疑),您可以将它们分解为自己的范围,然后在调用时将它们链接在一起。或者将它们链接在一起 in 另一个范围,然后调用它。就像我说的,Arel 真的很强大。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多