【问题标题】:Ruby on Rails SQL "SELECT" taking a long timeRuby on Rails SQL“SELECT”需要很长时间
【发布时间】:2015-10-05 06:26:56
【问题描述】:

我正在从包含超过 100,000 条记录的名为“bookings”的表中选择记录。我是 SQL 新手,由于某种原因,这需要几秒钟才能完成,甚至在我的生产服务器上超时:

def bookings_in_date_range(division, startdate, enddate)
  sql = "SELECT * FROM bookings WHERE division = '#{division}';"
  bookings = ActiveRecord::Base.connection.execute(sql) # all bookings from this division
  bookingsindaterange = bookings.select { |b| (parsedate(b["date"]) >= parsedate(startdate)) and (parsedate(b["date"]) <= parsedate(enddate)) } # refine to bookings in date range
end

def parsedate(date) # get date from mm/dd/yy format
  d = date.split("/")
  return Date.parse("#{d[2]}-#{d[0]}-#{d[1]}")
end

我还包含了我用来重新格式化日期的函数,但是根据我的测试,执行 SQL 语句似乎是进程挂起的地方。

我的目标是在指定日期范围内选择“部门”中的所有“预订”。对于预订数量较少的部门,现有代码的运行速度更快。

编辑

Otávio 下面的代码似乎加快了速度。但是,我的要求是查看预订是否在日期范围内(开始日期或之后结束日期或之前)。我不知道如何将这个逻辑放入 .where 语句中,所以我正在运行这样的循环:

    bookings_start_thru_end = []
    (startdate..enddate).each do |date|
      date_bookings = Booking.where("division = ? AND date = ?",division, date.strftime("%m/%d/%y"))
      date_bookings.each do |b|
        bookings_start_thru_end.push b
      end
    end

此外,崩溃的问题是 ActiveRecord 会话存储已满。我将报告中的一堆数据转储到会话存储中,以在请求之间保存它以避免执行另一个数据库查询,但这会降低性能。数据库查询仍然需要 5 秒左右,但我可以忍受。

【问题讨论】:

  • 超时前多少秒?描述服务器位置或任何相关信息(GoDaddy、就在您旁边等)
  • 这是一个内部服务器,具有足够的硬件,我在 Nginx 上运行 Phusion Passenger,数据库使用 Sqlite。根据我上面的编辑,我解决了由会话存储引起的超时问题,但是数据库请求仍然需要大约 5 秒,我认为这对于这种事情可能是正常的。
  • 感谢您分享会话存储
  • 是的,这让我陷入了困境。我认为会话存储是在请求之间存储数据的好地方,但是一旦我在那里转储了几千行,它就会严重降低整个应用程序的性能,几乎无法使用。根据我的阅读,如果我想在请求之间持久化生成的数据,我可能必须在数据库中创建一个模型并将其存储在那里。

标签: mysql ruby-on-rails ruby sqlite


【解决方案1】:

使用 EXPLAIN 查看查询执行计划是什么: https://dev.mysql.com/doc/refman/5.6/en/explain.html https://dev.mysql.com/doc/refman/5.6/en/using-explain.html

现在我的猜测是,您在 WHERE 中引用的列上没有索引,这会导致表扫描导致查询运行非常缓慢。但这只是我的猜测,因为我不知道你的表。

无论您使用的是原始 sql 还是活动记录 (spit),都需要索引。

【讨论】:

    【解决方案2】:

    应尽可能避免在应用程序中执行原始 SQL。更喜欢使用 ActiveRecord 接口,这不仅会使您的应用程序更安全,而且还会以优化的方式执行查询。 在您的情况下,重构您的 bookings_in_date_range 方法以使用 ActiveRecord 的 .where 方法:

    def bookings_in_date_range(division, enddate, startdate)
      YourModelName.where("division = ? AND enddate = ? AND startdate = ?",division, parsedate(enddate), parsedate(startdate))
    end
    

    要查找范围内的内容,请使用

    YourModelName.where("division = ? AND enddate <= ? AND startdate >= ?",division, parsedate(enddate), parsedate(startdate))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-11
      • 2015-10-23
      • 1970-01-01
      • 2016-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多