您正在跟踪事件的发生。事件(焊接)在某个时间开始并在某个时间结束。像这样对事件实体建模可能很诱人:
StationID StartTime StopTime
每个焊接站都有一个唯一的标识符。一些示例数据可能如下所示:
17 08:00:00 09:00:00
17 09:00:00 10:00:00
为简单起见,我将时间设置为较大的值(每个 1 小时)并删除了日期值。这告诉您,17 号焊接站在上午 8 点开始焊接并在上午 9 点完成,此时第二个焊接开始于上午 10 点结束。
这看起来很简单。但是请注意,第一个条目的 StopTime 与第二个条目的 StartTime 匹配。当然可以,一个焊接的结束标志着下一个焊接的开始。系统就是这样设计的。
但这设置了我所说的行跨越依赖反模式:行的一个字段的值必须与不同行中的另一个字段的值同步。
这会产生任何数量的问题。例如,如果第二个条目的 StartTime 显示“09:15:00”怎么办?现在我们在第一次焊接结束和下一次焊接开始之间有 15 分钟的间隔。该系统不允许有间隙——每次焊接结束时也会开始下一次焊接。应该如何解释这个差距。第一行的 StopTime 是否错误。第二行的StartTime错了吗?都错了吗?或者他们之间是否有另一行被某种方式删除了?没有办法判断哪个是正确的解释。
如果第二个条目的 StartTime 显示“08:45”怎么办?这是一个重叠,其中第二个周期的开始应该在第一个周期结束之前开始。同样,我们不知道哪一行包含错误数据。
跨行依赖允许间隙和重叠,这两者在数据中都是不允许的。需要大量的数据库和应用程序代码来防止这种情况发生,并且当它发生时(肯定会),没有办法确定哪些数据是正确的,哪些是错误的——不是从数据库内部,即。
一个简单的解决方案是完全取消 StopTime 字段:
StationID StartTime
17 08:00:00
17 09:00:00
每个条目都标志着焊接的开始。焊接的结束由下一个焊接的开始指示。这简化了数据模型,使得不可能有间隙或重叠,并且更精确地匹配我们正在建模的系统。
但是我们需要两行的数据来确定焊缝的长度。
select w1.StartTime, w2.StartTime as StopTime
from Welds w1
join Welds w2
on w2.StationID = w1.StationID
and w2.StartTime =(
select Max( StartTime )
from Welds
where StationID = w2.StationID
and StartTime < w2.StartTime );
这可能看起来像一个更复杂的查询,如果开始和停止时间在同一行 - 嗯,确实如此 - 但想想所有不再需要编写和在每个 DML 处执行的检查代码手术。而且由于 StationID 和 StartTime 的组合将是明显的 PK,因此查询将仅使用索引数据。
有一个补充建议。当天或休息后(如午餐)的第一次焊接和当天或休息前的最后一次焊接呢?我们必须努力不将休息时间作为循环时间。我们可以在查询中包含检测这种情况的智能,但这会进一步增加复杂性。
另一种方法是在记录中包含状态值。
StationID StartTime Status
17 08:00:00 C
17 09:00:00 C
17 10:00:00 C
17 11:00:00 C
17 12:00:00 B
17 13:00:00 C
17 14:00:00 C
17 15:00:00 C
17 16:00:00 C
17 17:00:00 B
所以前几个条目代表一个周期的开始,而中午和下午 5 点的条目代表休息的开始。现在我们只需要附加该行
where w1.Status = 'C'
到上面的查询结束。因此,“B”条目提供前一个周期的结束时间,但不开始另一个周期。