【问题标题】:Ruby on Rails 3 Check In/Check Out ranges by hourRuby on Rails 3 按小时签入/签出范围
【发布时间】:2011-10-10 15:26:36
【问题描述】:

我正在使用 Ruby on Rails 3,并且我有一个“访问”模型,它存储了 check_in 和 check_out 日期时间,我需要在一般日期范围内搜索访问并计算按所有人分组的“访问者”数量一天中的几个小时。

...即我需要类似的东西:

上午 8:00 - 上午 8:59:12 名访客 上午 9:00 - 上午 9:59:5 名访客 上午 10:00 - 上午 10:59 : 4 位访客

...给定一个存储了入住和退房时间的访问表。

我们的想法是记录“访问”的入住和退房时间,然后确定在一天中的任何给定时间有多少访问者(假设每次访问记录一个访问者,根据政策这样做)为了找出高峰访问时间。

我尝试设置如下查询:

eight_am_visits = Visit.where("EXTRACT(HOUR_MINUTE FROM check_in) <= 859").where("EXTRACT(HOUR_MINUTE FROM check_out) >= 800")

...并且还没有完全成功,因为 Rails 以一种奇怪的方式存储日期(在 UTC 中,它将在数据库查询中转换)并且当我使用某些东西时它似乎没有进行这种转换就像 SQL 中的 EXTRACT...

...知道我该怎么做吗?

【问题讨论】:

    标签: ruby-on-rails ruby range date-range hour


    【解决方案1】:

    看起来您实际上对 Visit 对象根本不感兴趣。如果你只是想要一个简单的总结,那就把 AR 推到一边,让数据库来做吧:

    # In visit.rb
    def self.check_in_summary(date)
        connection.select_rows(%Q{
            select extract(hour from check_in), count(*)
            from visits
            where cast(check_in as date) = '#{date.iso8601}'
            group by extract(hour from check_in)
        }).inject([ ]) do |a, r|
            a << { :hour => r[0].to_i, :n => r[1].to_i }
        end
    end
    

    然后a = Visit.check_in_summary(Date.today - 1) 会给你昨天的总结而不做任何额外的工作。当然,该演示实现会在数组中出现几个小时没有任何签入的漏洞,但这很容易解决(如果需要):

    def self.check_in_summary(date)
        connection.select_rows(%Q{
            select extract(hour from check_in), count(*)
            from visits
            where cast(check_in as date) = '#{date.iso8601}'
            group by extract(hour from check_in)
        }).each_with_object([0]*24) do |r, a| # Don't forget the arg order change!
            a[r[0].to_i] = r[1].to_i
        end
    end
    

    该版本返回一个包含 24 个元素(每个从零开始的小时一个)的数组,其值是该小时内的签到次数。

    在方便的时候不要害怕使用 SQL,AREL 只是一种工具,您的工具箱中应该有不止一种工具。此外,不要害怕向您的模型添加额外的数据处理和汇总方法,您的模型应该有一个接口,可以让您在其余代码中清楚地表达您的意图。

    【讨论】:

    • 这会考虑到 Rails 以 UTC 存储时间吗?不过,这给了我一些很好的见解,您的代码给我的是“每小时签到”而不是“任何给定时间的当前访问者数量”。我可以看到此时我可以如何对日期范围进行一些额外的过滤。
    • @ZroonStack:您可以在数据库中将check_in 时间转换为您需要的时区。您可以如上所述计算签入和签出,然后减去(在数据库内部或外部)以获得最终结果。
    【解决方案2】:

    也许是这样的?!

    t = Time.now
    eight_am_visits = Visit.all(:conditions => ['check_in > ? and check_in < ?', Time.utc(t.year, t.month, t.day, 8), Time.utc(t.year, t.month, t.day, 8, 59)])
    

    编辑: 或者您可以按天抓取所有访问并在 Rails 中过滤:

    t = Time.now
    visits = Visit.all(:conditions => ['created_at > ? and created_at < ?', Time.utc(t.year, t.month, t.day - 1), Time.utc(t.year, t.month, t.day + 1)])
    visits_by_hour = []
    (0..23).each do |h|
      visits_by_hour << visits.map {|e| e if e.created_at > Time.utc(t.year, t.month, t.day, h) && e.created_at < Time.utc(t.year, t.month, t.day, h, 59)}.count
    end
    

    并且在视图中:

    <% visits_by_hour.each_with_index do |h, v| %>
      <%= "#{h}:00 - #{h}:59: #{v} visitors" %>
    <% end %>
    

    【讨论】:

    • 很酷,但这是否意味着我可以使用 Rails 功能管理此问题的唯一方法是在所需的日期范围内循环遍历每一天并为每一天进行查询?
    • 我认为这里的棘手之处在于我必须每天在范围内循环以更新时间值。我现在正在尝试您的方法的一种变体,但是使用Time.new(1970, 1, 1, h).utc.strftime("%l%M").strip 之类的东西只是为了在正确的时区获得一个通用的 hour_minute 字符串,以便与 EXTRACT(HOUR_MINUTE FROM check_in) 等进行比较。我开始看到可能没有比像你在这里一样循环遍历每个小时更优雅的方式了...... o_O
    【解决方案3】:

    感谢您的帮助 Olexandr 和 mu,我设法根据您在这里给我的见解弄清楚了一些事情。

    我想出了这个,它似乎工作:

    #grab the data here, this is nice because 
    #I can get other stats out of it (which I don't show here)
    @visits = Visit.where(:check_in => @start_date..@end_date, :check_out => @start_date..@end_date).where("check_out IS NOT NULL");
    
    #Here we go
    @visitors_present_by_hour = {}
    (0..23).each do |h|
      # o.o Ooooooh.... o_o Hee-hee! ^_^
      @visitors_present_by_hour[h] = @visits.collect{|v| v.id if v.check_in.hour <= h and v.check_out.hour >= h}.compact.count
    end
    

    然后我可以在我的视图中转储该哈希。

    似乎解决方案比我想象的要简单一些,这样做实际上使 rails 可以从 UTC 进行时间转换。

    所以,我可以只收集小时范围内的所有访问,然后压缩 nil 并计算剩下的内容。我一碰上就惊呆了。我根本不需要任何自定义 SQL,因为我认为我会(除非这是完全错误的,但它似乎正在处理一些测试数据)。

    谢谢大家!

    【讨论】:

    • ...这里需要注意的是,这不包括跨越多天的访问,尽管这对我的数据并不重要,因为由于政策的原因,这从未发生过。如果你需要这个跨越多天,你可能需要一些更聪明的条件来收集......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-15
    • 2012-07-05
    • 2012-01-16
    相关资源
    最近更新 更多