5

我们正在构建一个应用程序来存储各种企业的“营业时间”。表示此数据的最简单方法是什么,以便您可以轻松检查项目是否打开?

一些选项:

  • 分割出可以标记为“打开/关闭”的块(每 15 分钟一次)。检查涉及查看“打开”位是否设置为所需时间(有点像火车时刻表)。
  • 存储时间范围列表(11am-2pm、5-7pm 等)并检查当前时间是否在任何指定范围内(这是我们的大脑在解析上述字符串时所做的事情)。

有没有人有存储和查询时间表信息的经验和任何建议?

(有各种疯狂的极端案例,比如“本月的第一个星期二关闭”,但我们将把它留到另一天)。

4

10 回答 10

5

将每个连续的时间块存储为开始时间和持续时间;这样可以更轻松地检查时间跨日期边界的时间

如果您确定营业时间永远不会跨越日期界限(即永远不会有通宵销售或 72 小时马拉松赛事等),那么开始/结束时间就足够了

于 2008-09-26T22:05:57.313 回答
3

最灵活的解决方案可能是使用 bitset 方法。一周有 168 个小时,所以有 672 个 15 分钟的时段。那只是 84 字节的空间,这应该是可以容忍的。

于 2008-09-26T22:06:10.587 回答
2

我会使用这样的表格:

BusinessID | weekDay | OpenTime | CloseTime 
---------------------------------------------
     1          1        9           13
     1          2        5           18
     1          3        5           18
     1          4        5           18
     1          5        5           18
     1          6        5           18
     1          7        5           18

在这里,我们有一家营业时间为 5 到 6 点的企业,但周日的营业时间较短。

如果打开的查询将是(psuedo-sql)

SELECT @isOpen = CAST
   (SELECT 1 FROM tblHours 
       WHERE BusinessId = @id AND weekDay = @Day 
       AND CONVERT(Currentime to 24 hour) IS BETWEEN(OpenTime,CloseTime)) AS BIT;

如果您需要存储边缘情况,那么只需要有 365 个条目,每天一个……在宏伟的计划中确实没有那么多,在 day 列和 businessId 列上放置一个索引。

不要忘记将企业时区存储在单独的表中(标准化!),并在进行这些比较之前在您的时间和它之间执行转换。

于 2008-09-26T23:15:06.523 回答
1

我想我个人会选择开始+结束时间,因为它会使一切变得更加灵活。一个很好的问题是:块大小在某个点发生变化的可能性有多大?然后选择最适合您情况的解决方案(如果它可能会改变,我肯定会选择时间跨度)。

您可以将它们存储为时间跨度,并在应用程序中使用段。这样,您就可以使用块轻松输入,同时保持数据存储更改的灵活性。

于 2008-09-26T23:09:02.230 回答
1

为了补充 Johnathan Holland所说的,我将允许同一天有多个条目。

我也将允许十进制时间,或分钟的另一列。

为什么?许多餐馆和一些企业,以及世界各地的许多企业都有午餐和/或下午休息时间。此外,许多餐厅(据我所知,我家附近的 2 家餐厅在奇怪的非 15 增量时间关闭。一家在周日晚上 9:40 关门,一家在凌晨 1:40 关门。

还有假期时间的问题,例如感恩节提前关门的商店,所以你需要有基于日历的覆盖。

也许可以做的是日期/时间打开,日期时间关闭,例如:

businessID  | datetime              | type
==========================================
        1     10/1/2008 10:30:00 AM    1
        1     10/1/2008 02:45:00 PM    0
        1     10/1/2008 05:15:00 PM    1
        1     10/2/2008 02:00:00 AM    0
        1     10/2/2008 10:30:00 AM    1

等(类型:1打开,0关闭)

并提前1-2年预先计算未来1或2年的所有日子。请注意,您将只有 3 列:int、日期/时间/位,因此数据消耗应该是最小的。

这也将允许您修改特殊日子的奇数小时的特定日期,因为它们是众所周知的。

它还负责跨越午夜以及 12/24 小时转换。

它也与时区无关。如果您存储开始时间和持续时间,当您计算结束时间时,您的机器是否会给您调整 TZ 的时间?那是你要的吗?更多代码。

至于查询开闭状态:查询有问题的日期时间,

select top 1 type from thehours where datetimefield<=somedatetime and businessID = somebusinessid order by datetime desc

然后看“类型”。如果为1,则为打开,如果为0,则为关闭。

PS:我从事零售业 10 年。所以我熟悉小企业的疯狂工作时间问题。

于 2008-09-26T23:31:18.137 回答
1

好的,我会为此付出代价。

我需要处理很多事情。

  • 快速/高性能查询
  • 任何时间增量,晚上 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 表的查询。

有趣的问题。我真的很惊讶这是我第一次真正需要考虑这个问题。一如既往,非常热衷于我的方法中的不同见解、方法或缺陷。

于 2013-11-19T06:45:50.803 回答
0

段块更好,只要确保你给用户一个简单的方法来设置它们。单击并拖动很好。

当您越过午夜边界时,任何其他系统(如范围)都会非常烦人。

至于如何存储它们,C++ 位域可能是最好的。在大多数其他语言中,数组可能会更好(浪费了大量空间,但运行速度更快,更容易理解)。

于 2008-09-26T22:20:53.533 回答
0

我现在会考虑一下这些边缘情况,因为它们会告知您是否有基本配置加上覆盖或开放时间的完整静态存储或其他。

有很多例外 - 并且定期(如下雪天,复活节等不规则假期,耶稣受难日),如果预计这是现实的可靠表示(而不是一个好的猜测),你需要很快在架构中解决它。

于 2008-09-26T23:12:38.527 回答
0

像这样的东西怎么样:

营业时间表

Business_id (int)
Start_Time (time)
End_Time (time)
Condition varchar/string
Open bit

'Condition' 是一个 lambda 表达式('where' 子句的文本)。动态构建查询。因此,对于特定业务,您可以选择所有的开/关时间

Let Query1 = select count(open) from store_hours where @t between start_time and end_time and open  = true and business_id = @id and (.. dynamically built expression)

Let Query2 = select count(closed) from store_hours where @t between start_time and end_time and open = false and business_id = @id and (.. dynamically built expression)

所以结束你想要的结果:

select cast(Query1 as bit) & ~cast(Query2 as bit)

如果最后一个查询的结果为 1,则商店在时间 t 开放,否则关闭。

现在您只需要一个可以为您生成 where 子句(lambda 表达式)的友好界面。

我能想到的唯一另一个极端情况是,如果一家商店在某个日期从早上 7 点营业到凌晨 2 点,但在下一个日期晚上 11 点关门,会发生什么情况。您的系统也应该能够通过巧妙地划分两天之间的时间来处理这个问题。

于 2008-09-27T00:12:04.513 回答
-1

这里肯定不需要节省内存,但可能需要干净且易于理解的代码。恕我直言,“有点玩弄”不是要走的路。

我们在这里需要一个集合容器,它包含任意数量的唯一项目,并且可以快速轻松地确定一个项目是否是成员。设置需要小心,但在日常使用中,一行简单易懂的代码决定了您是打开还是关闭

概念:从例如周日午夜开始,为每 15 分钟的区块分配索引号。

初始化:在打开时将每 15 分钟块的索引号插入一个集合。(假设您的营业时间少于关闭时间。)

使用:减去有趣的时间,以分钟为单位,减去前一个星期天的午夜,然后除以 15。如果这个数字出现在集合中,那么你是开放的。

于 2008-09-26T22:23:51.327 回答