【问题标题】:Rails and how make a subselect selecting idsRails 以及如何进行选择 id 的子选择
【发布时间】:2012-06-21 18:17:18
【问题描述】:

我是 Rails 新手,我知道这是一个简单的问题,但它让我发疯。 我的应用是用 Ruby 1.9.2 和 Rails 3.1 构建的。 我正在编写一个应用程序来管理类似酒店的客房预订。 所以这个类代表了住宿:

class Accommodation < ActiveRecord::Base
  has_many :reservations, :dependent => :destroy
end

这是预订:

class Reservation < ActiveRecord::Base
  belongs_to :accommodation
end

Reservation 确实有一个名为 accommodation_id 的字段,其中存储了住宿的 ID

现在我必须检查给定日期范围内的可用住宿,所以我写了这个方法:

def self.check_availability(params)
    # This return the unavailables accommodations
    ids_to_exclude = Reservation.select(:accommodation_id).where('start_date < ? AND end_date > ?', params[:reservation_end_date], params[:reservation_start_date])

    # HERE IS THE PROBLEM
    accommodations = Accommodation.where("id NOT IN (?)", ids_to_exclude )

    accommodations
  end

第一个查询有效,但第二个返回:

SELECT `accommodations`.* FROM `accommodation` WHERE (id NOT IN (NULL))

我尝试输出 ids_to_exclude 并且不是 NULL

我做错了什么?

更新:

我用 Arel 解决了这个问题。我在下面的评论中写了解决方案

【问题讨论】:

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


    【解决方案1】:

    ids_to_exclude需要提取id

    试试

     reservations_to_exclude = Reservation.select(:accommodation_id).where('start_date < ? AND end_date > ?', params[:reservation_end_date], params[:reservation_start_date])
    
     reservations_to_exclude.each do |r|
        ids_to_exclude << r.accommodation_id
     end
    

    【讨论】:

    • 感谢您的回复,但我找到了 Arel 避免 each do 块的解决方案
    【解决方案2】:

    您不需要为此进行两次数据库命中,并且您应该在此期间将其设置为范围。另外,我会调用将 params 传递给模型方法的错误形式,您应该明确说明参数以使事情更易于理解和维护:

    class Accommodation < ActiveRecord::Base
        def self.available_between(start_date, end_date)
            joins(:reservations).where('reservations.start_date >= ? and reservations.end_date <= ?', end_date, start_date)
        end
    end
    

    那么你可以这样说:

    all_available = Accommodation.available_between(params[:reservation_start_date], params[:reservation_end_date]).all
    

    或者这个:

    some = Accommodation.available_between(params[:reservation_start_date], params[:reservation_end_date])
                        .where(some_other_conditions)
                        .limit(10)
                        .order(:price)
    

    【讨论】:

    • 一开始我也想过加入,但由于加入的空保留,我放弃了这个想法。我刚刚试过你的方法,但没有结果,因为它建立了一个内部连接,所以不会出现没有预订的住宿
    • @DrDuke:您可以joins('left outer join reservations on ...') 手动强制左连接并将reservations.id is null 添加到where 条件。
    • 是的,你是对的:我可以手动强制加入,但我正在寻找没有手动强制且不使用to_sql 的东西。无论如何谢谢:)
    • 我再次尝试了这个查询,它返回了有预订的住宿(我正在寻找相反的)。我对 Arel 的查询不起作用,因为返回的结果与您的相同:/
    【解决方案3】:

    最后我找到了 Arel 的解决方案:

    reservations = Reservation.arel_table
    
    available_rooms = Accommodation.where(:id => reservations.project(:accommodation_id)
          .where( reservations[:start_date].lt(params[:reservation_end_date]) )
          .where( reservations[:end_date].gt(params[:reservation_start_date]) ))
    

    【讨论】:

    • 这行不通,因为我需要 id 不在子查询中的住宿。试图调整查询我有一个错误说:Cannot visit Arel::SelectManager
    猜你喜欢
    • 2021-06-02
    • 2017-06-03
    • 2011-03-28
    • 1970-01-01
    • 2018-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多