M:N表的DB设计,具有时间间隔

时间:2014-07-02 22:17:00

标签: mysql sql

我想问你一个设计问题:

我正在设计一张桌子让我头脑发热,不知道最好的方法是什么,我觉得我错过了一些东西:

它们之间有两个表A和B以及一个M:N关系表。关系表现在具有以下值: A.ID,B.ID,From,To

业务要求: 在任何时候,A:B关系船只能只有1:1 答:B可以按照From和To datetime定义的时间重复,这些值指定间隔

示例:汽车/司机。 任何车辆在任何时候都只能有1个车手 任何车手都可以随时开出一辆车(这不是顶级车,好吗?:)) 司机可以在一段时间后更换汽车,并可以返回同一辆车

现在,我不确定: - 我应该带什么PK? A,B是不够的,添加From和To感觉不对,也许是自动增量PK? - 通过数据库设计强制执行业务需求的方法吗? - 出于商业原因,我宁愿不在历史表中。为什么?好吧,让我们假设这辆车是租来的,我想知道,在约会的日期,谁拥有那天租用的汽车。将其拆分为历史表需要更多的连接:(

我觉得我错过了某种东西,某种普遍的模式......或者我不知道......

感谢任何帮助,谢谢:)

3 个答案:

答案 0 :(得分:0)

我认为你实际上没有遗漏任何东西。我想你已经掌握了问题所在。

我读过几篇关于如何在关系数据库中处理“时态”数据的文章。

底线共识是传统的关系模型没有任何支持时态数据的内置机制。

有几种方法,有些方法比其他方法更适合特殊要求,但所有方法都感觉它们是“管道录音”。

(我打算说“狂奔”,但我认为在红色绿色的帽子的尖端是有序的:“......杂工的秘密武器,胶带”,“如果女人不发现你很帅,他们至少应该找到你方便的。“)


就表的PRIMARY KEY或UNIQUE KEY而言,您可以使用(a_id, b_id, from)的组合。这将为行提供唯一标识符。

但是,这并不能阻止重叠的“时间”范围。

MySQL表没有声明性约束,可以防止“重叠”日期时间范围存储为“开始”,“结束”或“开始”,“持续时间”等。(至少在一般情况下。如果您有非常明确定义的范围,并且触发器将from四舍五入到四小时的边界,并且持续时间精确到四小时,则可以使用UNIQUE约束。在更一般的情况下,对于任何ol'值fromto,UNIQUE约束对我们不起作用。

CHECK约束是不够的(因为您需要查看其他行),即使可能,MySQL也不会实际执行检查约束。

让数据库强制实施这种约束的唯一方法(我知道)是TRIGGER,它会查找受影响(插入/更新)行会发生冲突的另一行的存在。 / p>

您需要BEFORE INSERT触发器和BEFORE UPDATE触发器。触发器需要查询表,检查是否存在“重叠”新行/修改行的行

SELECT 1
  FROM mytable t
 WHERE t.a_id = NEW.a_id
   AND t.b_id = NEW.b_id
   AND t.from <> OLD.from
   AND < (t.from, t.to) overlaps (NEW.from,NEW.to) >

显然,最后一行是伪代码,用于实际所需的语法。

之前的行仅在BEFORE UPDATE触发器中需要,因此我们找不到(作为“匹配”)正在更新的行。那里的实际检查实际上取决于PRIMARY KEY(或UNIQUE KEY)(s)的选择。

使用MySQL 5.5,如果我们发现新的/更新的行会违反约束,我们可以使用SIGNAL语句返回错误。使用以前版本的MySQL,我们可以通过执行导致实际错误发生的事情来“抛出”错误,例如针对我们知道不存在的表名运行查询。

最后,这种类型的功能不一定必须在数据库触发器中实现;这可以在客户端处理。

答案 1 :(得分:0)

三张桌怎么样: TCar,TDriver,TLog

TCAR pkCarID fkDriverID 名称

驾驶员的独特索引确保驾驶员只能驾驶一辆车。转动外键 fkDriverID成1:1的关系。

TDriver pkDriverID 名称

的TLog pkLogID(代理pk) fkCarID fkDriverID 从 到

通过2个连接,您将获得您描述的任何信息。如果你只是需要通过cardID找到carID数据或通过cardid找到驱动程序数据,你可以通过一个连接来完成。

答案 2 :(得分:0)

谢谢大家的意见,到目前为止,我正在考虑这种方法,感谢任何批评/指出缺陷: 表(pseudoSQLcode):

Car   (ID pk auto_increment, name)
Driver(ID pk auto_increment, name)
Assignment (CarID unique,DriverID unique,from Datetime), composite PK (CarID,DriverID)
AssignmentHistory (CarID unique,DriverID unique,from Datetime,to Datetime) no pk

当然,CarID是FK到Car(ID),DriverID是FK到驱动程序(ID)

下一个阶段是两个触发器(和男孩哦男孩,我希望这可以在mysql中完成(适用于MSSSQL,但我现在没有一个mysql db方便测试):

!!!警告,MSSQL现在

create trigger Assignment _Update on Assignment instead of update as
    delete Assignment 
      from Assignment 
           join inserted 
             on (    inserted.CarID= Assignment .CarID
                  or inserted.DriverID= Assignment .DriverID)
                and ( inserted.CarID<> omem.CarID or inserted.DriverID<> omem.DriverID)
    insert into Assignment 
    select * from inserted;



create trigger Assignment _Insert on Assignment  after delete as
    insert into Assignment_History
    select CarID,DriverID,from,NOW() from deleted;

我测试了一下,似乎每个商务案例它都做我需要做的事情