我从名为“预订”的表中选择包含超过100,000条记录的记录。我是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秒左右,但我可以忍受。
答案 0 :(得分:1)
应尽可能避免在应用程序中执行原始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))
答案 1 :(得分: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),都需要索引。