我很难创建一个高性能的数据库 Select . 以下 case :

A booking has 2 attributes departure and arrival.
Both attributes are type date. I want to select all Persons with bookings with departure: 1.month.ago and no Booking in the future.

Relation:

class Booking
   has_many :persons, class_name: 'Person', through: :stays
end

class Stay
  belongs_to :booking, class_name: 'Booking'
  belongs_to :person, class_name: 'Person'
end

class Person
   has_many :bookings, class_name: 'Booking', through: :stays
end

因为数据库中有大约3.800.000个人,所以我不能在 Select 之后使用ruby逻辑进行 Select .

我试过一些请求,但我没有得到任何结果,即使有一些.

Person.joins(:bookings).where('bookings.departure < ? AND NOT EXISTS (SELECT 1 FROM bookings WHERE arrival >= ?)', 1.month.ago, Time.zone.today).find_each(&:clear)

是否可能有这样的数据库请求,或者我需要在活动记录中处理一个 case ,而其他 case 则通过逻辑处理?

Person.joins(:bookings).where('bookings.departure < ?', 1.month.ago).find_each do |pers|
  pers.clear unless pers.bookings.where('arrival >= ?', Time.zone.today).exists?  
end

推荐答案

似乎你只是缺少了一个连接和后续的过滤条件:

NOT EXISTS (SELECT 1 FROM bookings WHERE arrival >= ?)

为了确保此过滤器基于特定的人,您需要将其更改为:

NOT EXISTS (
  SELECT 1 
  FROM bookings 
  INNER JOIN stays ON stays.booking_id = bookings.id 
  WHERE arrival >= ? AND stays.person_id = people.id)

我们应该能够在ActiveRecord/Arel中转换这个查询,如下所示:

Person.joins(:bookings)
  .where(bookings: {departure: ...1.month.ago})
  .where.not(
      Stay.select(1)
        .joins(:bookings)
        .where(bookings: {arrival: Time.zone.today..})
        .where(Stay.arel_table[:person_id].eq(Person.arel_table[:id]))
        .arel.exists
  )

这将导致以下查询

SELECT 
  people.*
FROM 
  people 
  INNER JOIN stays ON stays.person_id = people.id
  INNER JOIN bookings ON bookings.id = stays.booking_id
WHERE 
  bookings.departure < '2024-02-26' 
AND 
  NOT (
    EXISTS (
      SELECT 1 
      FROM 
        stays
        INNER JOIN bookings ON bookings.id = stays.booking_id 
      WHERE 
        stays.person_id = people.id 
        AND bookings.arrival >= '03-26-2024'))

或者,你可以使用两个单独的IN子句.代码稍微干净一些,但可能会对性能产生影响.

Person
  .where(id: 
    Stays.joins(:bookings).select(:person_id).where(bookings: {departure: ...1.month.ago}))
  .where.not(id: 
    Stays.joins(:bookings).select(:person_id).where(bookings:{arrival: Time.zone.today..}))

SQL:好的,好的.

SELECT 
  people.*
FROM 
  people 
WHERE 
  people.id IN ( 
    SELECT 
      stays.person_id
    FROM 
        stays
        INNER JOIN bookings ON bookings.id = stays.booking_id 
      WHERE 
        bookings.departure < '2024-02-26') 
AND 
  people.id NOT IN ( 
    SELECT 
      stays.person_id
    FROM 
        stays
        INNER JOIN bookings ON bookings.id = stays.booking_id 
      WHERE 
        bookings.arrival >= '03-26-2024')

Ruby-on-rails相关问答推荐

如何测试自定义路由?

Ruby线程使用互斥处理并发问题

微软安全链接在页面加载前解锁用户

Rails 和 jsonb 类型jsonb不存在

Rails - 如何使用子对象的嵌套属性和强参数填充父对象ID?

使用rails form_for时带有_path的未定义方法

为什么使用 HTTP PUT 和 DELETE 方法而不是 POST?

Rails 模型:您将如何创建一组预定义的属性?

从子类中的重载方法调用基类方法

不带参数的 request.fullpath

Rails - 如何在 f.submit 上放置确认弹出窗口?

使用回形针使用 Activeadmin Rails 上传文件

如何在Ruby 中对数字进行上限和舍入

如何检测我的代码是否在 Rails 3 的控制台中运行?

默认呈现 JSON 而不是 HTML?

如何在 VS Code 中自动格式化 Ruby 或 .erb 文件?

如何为 rspec 设置 ENV 变量?

Heroku - 在浏览器中显示当前提交的哈希

如何在 Rails 迁移中判断数据库类型?

我的 JavaScript 模式/实践很糟糕.我应该go 哪里寻求帮助?