如何从日期范围查询中查找表中的一组缺少日期

时间:2012-12-17 05:32:38

标签: ruby-on-rails gaps-and-islands rails-postgresql

有一些与我的问题相关的stackoverflow帖子,但并非完全相似。

我希望在将用户指定的日期范围与postgresql中的汇总表进行比较后,获得一个有效且有点优雅(如果可能)的解决方案,以获取缺少日期的数组。我知道的一种方法是将范围放入日期列表中,然后通过查询EXIST或者结果== nil?/ empty?等单独比较所有日期。但是如果用户要做大范围,这可能是资源消耗和缓慢。

除了目前列出的方法之外还有其他方法吗?

谢谢

1 个答案:

答案 0 :(得分:0)

首先,我们需要对日期进行排序。在ruby中,这就像

一样简单
sorted_dates = dates.sort

如果您知道日期已排序,则只需从第一个日期开始,并在迭代日期范围时按1递增。如果数组中的下一个日期不是您期望的日期,请将缺少的日期添加到missing_dates数组中,然后继续递增,直到达到所包含的日期。

此代码可能如下所示:

def find_missing_dates(sorted_dates)
  current_date = sorted_dates[0]
  missing_dates = Set.new
  sorted_dates.each do |date|
    while current_date != date
      missing_dates << current_date
      current_date += 1.day
    end
    current_date += 1.day
  end
end

对于普通情况,这是O(N),所以为了提高效率,我们可以分成两半并递归。

def dates_between(lower, upper)
  (lower..upper).to_a - [lower,upper]
end

def find_missing_dates(sorted_dates, missing_dates = Set.new)
    min_date = sorted_dates[0]
    max_date = sorted_dates[-1]
    if (min_date - max_date).to_i == (sorted_dates.count - 1)
      missing_dates
    else
      middle_date_lower = sorted_dates[sorted_dates.count / 2 - 1]
      middle_date_upper = sorted_dates[sorted_dates.count / 2]
      unless (middle_date_upper - middle_date_lower) == 1
        missing_dates.merge(dates_between(middle_date_lower, middle_date_upper))
      end
      find_missing_dates(sorted_dates[0..(sorted_dates.count/2 - 1)], missing_dates).merge(find_missing_dates(sorted_dates[(sorted_dates.count/2)..-1]))
    end
end

find_missing_dates(sorted_dates)

这仍然是最坏情况O(N),但是平均情况是O(log N)