好的,我会为此付出代价。
我需要处理很多事情。
- 快速/高性能查询
- 任何时间增量,晚上 9:01、12:14 等。
- 国际(?) - 不确定这是否是时区的问题,至少在我的情况下,但更精通这里的人可以随意插话
- 打开 - 关闭到第二天(中午开放,凌晨 2:00 关闭)
- 多个时间跨度/天
- 能够覆盖特定日期(节假日等)
- 覆盖重复发生的能力
- 能够查询任何时间点并使业务开放(现在、未来时间、过去时间)
- 能够轻松排除即将关闭的企业的结果(过滤在 30 分钟内关闭的企业,您不想让您的用户成为在食品/饮料行业关闭前 5 分钟出现的那个人)
我喜欢很多提出的方法,我从其中一些方法中借鉴。在我的网站、项目中,无论我需要考虑什么,我都可能拥有数百万家企业,而这里的一些方法对我个人来说似乎并不能很好地扩展。
这是我对算法和结构的建议。
我们必须在全球任何地方、任何时间做出一些具体的假设:一周有 7 天。一天有1440分钟。可能的打开/关闭分钟数的排列是有限的。
不具体但体面的假设:开放/关闭分钟的许多排列将在企业之间共享,从而减少实际存储的总排列。在我的生活中有一段时间,我可以很容易地计算出这种方法的实际可能组合,但如果有人可以提供帮助/认为它会有用,那就太好了。
我建议 3 个表:在您停止阅读之前,请考虑在现实世界中,这些表中的 2 个将足够小且整齐地缓存。这种方法也不适合所有人,因为将 UI 解释为数据模型并在需要时再次返回所需的代码非常复杂。您的里程和需求可能会有所不同。这是对合理的“企业”级解决方案的尝试,无论这意味着什么。
营业时间表
身份证 | 开盘(一天中的分钟)| 关闭(一天中的分钟)
1 | 360 | 1020(例如:上午 9 点 - 下午 5 点)
2 | 365 | 1021(例如:边缘情况 9:05 AM - 5:01 PM(怪异))
等等
HoursOfOperations 不关心什么日子,只关心打开和关闭和唯一性。每个打开/关闭组合只能有一个条目。现在,根据您的环境,可以缓存整个表,也可以缓存当天的当前时间,等等。无论如何,您不需要为每个操作查询此表。根据您的存储解决方案,我设想此表中的每一列都作为性能索引。随着时间的推移,该表可能具有 INSERT(s) 的指数倒数可能性。实际上,处理这个表应该主要是一个进程内操作(RAM)。
Business2Hours地图
注意:在我的示例中,我将“Day”存储为位标志字段/列。这主要是由于我的需要和 C# 中 LINQ / Flags Enums 的进步。没有什么能阻止您将此扩展到 7 位字段。两种方法在存储逻辑和查询方法上都应该相对相似。
另一个注意事项:我没有进入关于“每个表都需要一个 PK ID 列”的语义论点,请为此查找另一个论坛。
商号 | 营业时间ID | 日(或者,如果您更愿意拆分为:BIT 星期一、BIT 星期二,...)
1 | 1 | 1111111(此业务每周9-5开放)
2 | 2 | 1111110(此业务开放时间为 9:05 - 5:01 M-Sat(星期一 = 第 1 天)
这很容易查询的原因是我们总是可以很容易地确定我们所追求的 MOTD(一天中的分钟)。如果我想知道明天下午 5 点开放什么,我会获取所有 HoursOfOperations IDS WHERE Close >= 1020。除非我正在寻找时间范围,否则 Open 变得无关紧要。如果您不想显示在接下来的半小时内关闭的商家,只需相应地调整您的传入时间(搜索 5:30 PM (1050),而不是 5:00 PM (1020)。第二个查询自然是 '用 HoursID IN (1, 2, 3, 4, 5) 等给我所有业务。这可能会引发危险信号,因为这种方法存在局限性。但是,如果有人可以回答上面的实际排列问题,我们可能会能够拉下红旗。考虑到我们一次只需要等式任意一侧的可能排列,
考虑到我们已经缓存了第一个表,这是一个快速的操作。第二个操作是查询这个潜在的大行表,但我们正在搜索非常小的 (SMALLINT) 希望索引列。
现在,您可能会看到代码方面的复杂性。我主要针对我的特定项目中的酒吧,因此可以非常安全地假设我将拥有大量营业时间,例如“11:00 AM - 2:00 AM(第二天)”。这确实是 HoursOfOperations 表和 Business2HoursMap 表中的 2 个条目。例如,从上午 11:00 - 凌晨 2:00 营业的酒吧将有 2 次参考 HoursOfOperations 表 660 - 1440(上午 11:00 - 午夜)和 0 - 120(午夜 - 凌晨 2:00)。这些引用将反映到 Business2HoursMap 表中的实际天数中,在我们的简单案例中作为 2 个条目,1 个条目 = 所有天小时参考 #1,另一个全天参考 #2。希望这是有道理的,这是漫长的一天。
在特殊的日子/假期/任何事情上都可以覆盖。覆盖本质上是基于日期的,而不是基于星期几的。我认为这就是一些方法试图将众所周知的圆形钉子推入方孔的地方。我们需要另一张桌子。
营业时间ID | 商号 | 天 | 月 | 年
1 | 2 | 1 | 1 | 空值
如果您需要“每个第二个星期二,这家公司钓鱼 4 小时”之类的东西,这肯定会变得更加复杂。然而,这将让我们很容易做到的是允许 1 - 覆盖,2 - 合理的重复覆盖。例如,如果 year IS NULL,那么每年元旦这个怪异酒吧的营业时间为上午 9:00 到下午 5:00,与我们上面的数据示例保持一致。即 - 如果设置了年份,则仅适用于 2013 年。如果月份为空,则为每月的第一天。同样,这不会仅通过 NULL 列处理每个调度方案,但理论上,如果需要,您可以通过依赖一长串绝对日期来处理几乎任何事情。
同样,我将在滚动日的基础上缓存此表。我只是无法在单日快照中实际看到该表的行非常大,至少对于我的需要而言。我会首先检查这个表,因为它是一个覆盖,并将保存一个针对存储端更大的 Business2HoursMap 表的查询。
有趣的问题。我真的很惊讶这是我第一次真正需要考虑这个问题。一如既往,非常热衷于我的方法中的不同见解、方法或缺陷。