【问题标题】:Opening Hours Database Design开放时间数据库设计
【发布时间】:2011-01-25 17:54:58
【问题描述】:

我们目前正在开发一个应用程序,其中多个实体都有相关的营业时间。营业时间可能跨越多天,也可能包含在一天之内。

例如。周一 6:00 开门,周五 18:00 关门。

或者

周一 06:00 开放,周一 15:00 关闭。

此外,一个实体每天可能有多组营业时间。 到目前为止,我发现的最佳设计是将开放时间定义为以下内容:

StartDay、StartTime、EndDay 和 EndTime。

这种设计允许所有需要的灵活性。但是,数据完整性成为一个问题。我似乎找不到(在数据库中)不允许重叠跨度的解决方案。

请分享你的想法。

编辑:数据库是 Microsoft SQL Server 2008 R2

【问题讨论】:

  • 您使用的是什么数据库系统?这将有助于定义我们可以提供的解决方案(通过约束、触发器、计算字段等)

标签: sql database timespan data-integrity integrity


【解决方案1】:

考虑存储您的 StartDay 和 StartTime,然后为其打开的小时数设置一个值。这将确保您的结束日期时间在开幕之后。

OpenDate -- day of week? e.g. 1 for Monday
OpenTime -- time of day. e.g. 08:00
DurationInHours -- in hours or mins. e.g.   15.5

【讨论】:

  • 我也考虑过这个解决方案。然而,虽然它解决了单个开放时间定义的时间跨度重叠问题,但当一个实体有多个开放时间定义时,问题仍然存在。
【解决方案2】:

假设一个健壮的触发器框架

在插入/更新时,您将检查新的开始日期或结束日期是否在任何现有范围内。如果是这样,那么您将回滚更改。

CREATE TRIGGER [dbo].[mytable_iutrig] on [mytable] FOR INSERT, UPDATE AS

IF (SELECT COUNT(*)
FROM inserted, mytable
WHERE (inserted.startdate < mytable.enddate 
          AND inserted.startdate > mytable.startdate)
      OR (inserted.enddate < mytable.enddate 
          AND inserted.enddate > mytable.startdate)) > 0 
BEGIN
    RAISERROR --error number
    ROLLBACK TRANSACTION
END

【讨论】:

  • 由于在执行插入或更新后触发了触发器,我将不得不以某种方式回滚更改。就像你描述的那样。看来我不能确定删除正确的插入行,我也看不到回滚更新的方法?
  • 这似乎是一个非常棒的解决方案。能否确保并发问题不会导致触发器在错误的时间执行?
  • 这就是 TRANSACTION 部分的用武之地。SQL 在单独处理每个事务方面做得很好,这样回滚对表的一个调用不会影响同时对同一个表的其他调用。
  • 如果inserted.startdate
【解决方案3】:

必须在应用程序级别检测和防止重叠时间段。当然,您可以尝试在数据库中使用触发器,但我认为这不是数据库问题。您提出的结构很好,但您的应用程序逻辑必须处理重叠。

【讨论】:

  • 这自然是一个可能的解决方案。但是,如果这样的解决方案完全可行,我更愿意确保不可能出现不一致的数据。
【解决方案4】:

Joe Celko 在 SimpleTalk 网站上发表了一篇关于 here 的文章,讨论了类似的问题,并提出了一个优雅但复杂的解决方案。这可能适用于您的情况。

【讨论】:

    【解决方案5】:

    具有单列 TimeOfChangeBetweenOpeningAndClosing 的表?

    不过,更严重的是,我可能不会太担心提出一个单一的数据库结构来表示所有内容,最终您可能想要一个涉及重复、计划关闭等的系统。持久化表示这些的对象,然后评估他们找出关闭/开放时间。

    【讨论】:

    • 只存储增量值如何防止重叠?
    • 您必须有一个参考日作为您的起点,但这可能不是一个糟糕的解决方案。我在这里看到的最大问题是,要调整一个时间段,您还必须调整其两侧的两个时间段以进行补偿。
    • +1 用于跳出框框思考并将问题引向全局。
    【解决方案6】:

    这看起来是一个不错的解决方案,但您必须编写自定义验证函数。内置的数据库验证(即唯一性、小于 x 等)不会在这里解决问题。为确保您没有重叠跨度,每次将记录插入数据库时​​,您都必须选择现有记录并进行比较...

    【讨论】:

    • 是否可以在数据库中编写这种验证,还是必须将其放在应用程序级别的业务逻辑中?
    • 您可以在数据库级别编写它,例如@Russell Steen 建议的。
    【解决方案7】:

    首先是逻辑,如果一个的开始值落在另一个的开始/结束之间,则两个跨度将重叠。如果我们将日期时间结合起来,而不是 date1,time1 和 date2,time2,这会容易得多。因此,查找重叠的查询如下所示。

    select openingId
      from opening o1
      join opening o2 on o1.startDateTime 
                 between o2.startDateTime
                     AND o2.endDateTime
    

    您可以将其放入触发器中,如果找到匹配项则抛出错误。

    【讨论】:

      猜你喜欢
      • 2019-11-13
      • 2010-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-18
      • 2011-12-07
      • 2019-10-01
      相关资源
      最近更新 更多