【问题标题】:Efficiently determining if a business is open or not based on store hours根据商店营业时间有效地确定企业是否营业
【发布时间】:2010-10-20 23:35:29
【问题描述】:

给定一个时间(例如,目前是周二下午 4:24),我希望能够从一组企业中选择所有当前营业的企业。

  • 我有一周中每一天的每家企业的营业和关闭时间
  • 假设一家企业只能在每小时的 00、15、30、45 分钟标记开/关
  • 我假设每周的时间表相同。
  • 我最感兴趣的是能够快速查找在特定时间开放的一组业务,而不是数据的空间要求。
  • 请注意,有些我一天晚上 11 点开门,第二天凌晨 1 点关门。
  • 节假日无所谓 - 我会单独处理这些

存储这些打开/关闭时间的最有效方法是什么,以便通过单个时间/星期几元组我可以快速找出哪些企业正在营业?

我正在使用 Python、SOLR 和 mysql。我希望能够在 SOLR 中进行查询。但坦率地说,我愿意接受任何建议和替代方案。

【问题讨论】:

  • 您对查找业务的效率更感兴趣,还是对占用更少的存储空间更感兴趣?
  • 您需要更清楚地说明您的要求。你需要什么样的粒度? (企业可以在 7:36 营业吗?)每周是否足够或有特殊情况(每隔一个星期五休息)
  • 还有节假日(银行假日、国定假日)呢?还是公司休假等奇怪的事情?
  • 也许这不是最快的方法,但一个简单而干净的方法就是使用嵌套查询.....如果你想找到谁在周二 4:24 开门,然后找到所有的商家都在星期二营业,然后与 4:24 之前的营业时间相交,并与 4:24 之后的营业时间相交。即,您的表格看起来像:store_id、day_of_week、open_time、close_time,并且您可能每个商店都有 5 个条目...

标签: python mysql performance solr


【解决方案1】:

如果您愿意一次只查看一周,您可以将所有开/关时间规范化为自一周开始以来设置的分钟数,例如周日 0 小时。对于每个商店,您创建多个 [startTime, endTime, storeId] 形式的元组。 (对于跨越周日午夜的几个小时,您必须创建两个元组,一个到周末,一个从一周开始)。这组元组将在 startTime 和 endTime 上都被索引(例如,使用您将预处理的树)。元组不应该那么大:一周只有大约 10k 分钟,可以容纳 2 个字节。这种结构在具有适当索引的 MySQL 表中将是优雅的,并且在信息更改时对不断插入和删除记录非常有弹性。您的查询将只是“select storeId where startTime = time”,其中 time 是自周日午夜以来的规范化分钟数。

如果信息不经常更改,并且您希望查找速度非常快,则可以预先解决所有可能的查询并缓存结果。例如,一周只有 672 个刻钟。有了一个企业列表,每个企业都有一个像 Brandon Rhodes 的解决方案一样的开放和关闭时间列表,您可以简单地在一周内每隔 15 分钟迭代一次,找出谁在营业,然后将答案存储在查找表中或内存列表。

【讨论】:

  • 这是一个很好的解决方案。我可能会将查询留给 SOLR,但这可能是我的查找表。谢谢!
  • 显然这对问题作者来说没有问题,但根据我的经验,营业时间问题的真正困难部分是一次性例外,例如假期和一次性额外开放(只有一家商店)。
【解决方案2】:

另一位受访者提到的位图字段将非常有效,但如果您希望能够处理半小时或四分之一小时的时间,则会变得混乱,因为您必须在算术上增加位数和设计每次遇到必须匹配的新分辨率时,该字段。

我会尝试将值作为日期时间存储在列表中:

openclosings = [ open1, close1, open2, close2, ... ]

然后,我将在其内置的“bisect”模块中使用 Python 的“bisect_right()”函数,以在 O(log n) 时间内快速找到您的查询时间“适合”的列表中的哪个位置。然后,查看返回的索引。如果它是偶数(0、2、4...),则时间介于“关闭”时间之一和下一个“开放”时间之间,因此商店关闭。相反,如果二等分索引是奇数(1、3、5...),则时间已落在开店时间和关店时间之间,商店开张了。

没有位图那么快,但你不必担心分辨率,而且我想不出另一个如此优雅的 O(log n) 解决方案。

【讨论】:

  • 是的。当然,要考虑历史数据的问题,但这是另一回事。
  • 我非常喜欢这个解决方案,非常优雅。我唯一的意见是你可以使用 bisect() 作为 bisect_right() 的别名。
  • 这是一个很好的解决方案,但我在我的问题中犯了一个错误。我希望查询一个数据集并取回所有开放的业务。我错误地将其表述为测试单个企业是否开放。
【解决方案3】:

您说您正在使用 SOLR,不关心存储,并且希望查找速度快。然后,不要存储打开/关闭元组,而是以您需要的粒度级别(15 分钟)为每个打开的时间块索引一个条目。对于编码本身,您可以只使用累计小时:分钟。

例如,周一下午 4 点到 5 点营业的商店会为 [40:00, 40:15, 40:30, 40:45] 添加索引值。周一下午 4:24 的查询将被规范化为 40:15,因此与该商店文档匹配。

乍一看,这可能看起来效率低下,但它对索引速度和空间的影响相对较小。并尽可能快地进行搜索。

【讨论】:

    【解决方案4】:

    抱歉,我没有一个简单的答案,但我可以告诉你,作为 90 年代后期公司开发团队的经理,我们的任务是解决这个问题,这很困难。

    每周的营业时间并不困难,可以使用相对较小的位掩码(168 位 = 每周 1 次)来完成,诀窍是每周二交替关闭的企业。

    从位掩码开始,然后转到异常字段是我见过的最佳解决方案。

    【讨论】:

    • 实际的 SQL 会是什么样子?
    • 我的假设是,如果你要做这样的事情,你会编写自己的索引器和比较器。
    【解决方案5】:

    在您的 Solr 索引中,不要将每个业务作为一个以小时为单位的文档进行索引,而是在一周内为每个业务的每个“零售会话”建立索引。

    例如,如果 Joe 的咖啡店在周一至周六上午 6 点至晚上 9 点营业,周日不营业,您将索引六个不同的文档,每个文档都有两个索引字段,“open”和“close”。如果您的单位是 15 分钟间隔,则值的范围可以从 0 到 7*24*4。假设您对每个企业都有一个唯一的 ID,请将其存储在每个文档中,以便您可以将会话映射到企业。

    然后你可以简单地在 Solr 中进行范围搜索:

    打开:[* TO N] 并关闭:[N+1 TO *]

    其中 N 计算为当前时间所在的第 N 个 15 分钟间隔。例如,如果是星期三上午 10:10,您的查询将是:

    打开:[* TO 112] 并关闭:[113 TO *]

    又名“查找在周三上午 10:00 或之前开始并在周三上午 10:15 或之后结束的会话”

    如果您想在搜索中包含其他条件,例如位置或产品,您还需要将其与每个会话文档一起编入索引。这有点多余,但如果你的索引不是很大,那应该不是问题。

    【讨论】:

    • 猜猜我没有仔细阅读塞巴斯蒂安的答案。我的回答与他的概念基本相同,但在 Solr 中实现。
    • 别担心!我认为这很棒。我不会想到这样做。我想我可能需要指定其他查询参数,如“cuisine”等,所以我可能需要为每个业务编制索引时间间隔。
    【解决方案6】:

    如果你能很好地控制你的数据,我看到了一个简单的解决方案,类似于@Sebastian 的。遵循创建元组的建议,除了以 [time=startTime, storeId] 和 [time=endTime, storeId] 的形式创建它们,然后在列表中对它们进行排序。要了解商店是否营业,只需执行如下查询:

    select storeId
    from table
    where time <= '@1'
    group by storeId
    having count(storeId) % 2 == 1
    

    为了优化这一点,您可以在每个时间 t 建立一个查找表,存储在 t 开放的商店,以及 t 和 t+1 之间的商店开放/关闭(对于 t 的任何分组)。

    但是,这样做的缺点是更难维护(重叠的打开/关闭需要合并到更长的打开-关闭期间)。

    【讨论】:

      【解决方案7】:

      您是否查看过有多少种独特的开/关时间组合?如果数量不多,则制作唯一组合的参考表,并针对每个业务存储相应条目的索引。然后您只需搜索参考表,然后找到具有这些索引的业务。

      【讨论】:

      • 这是一个有趣的解决方案——我之前没有想过让间隔成为离散的 #,但你是对的——如果你假设大多数企业开/关时间,这完全可以预先计算30 分(或 15 如果你想变得更漂亮)
      • 我不知道您的问题,但您甚至可能会发现只有少数几种组合。不久前我看过类似的东西 - 80% 是 M-F,9-5。有些在星期六开门,营业时间是 10 点到 2 点、4 点或 5 点。星期天同样的营业时间更少。几个周末的晚上。总共只有十几个开放时间组合
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-09
      • 1970-01-01
      相关资源
      最近更新 更多