数据库约束

时间:2014-12-13 23:41:09

标签: database database-design uml relational-database database-schema

我有一个包含三个表的数据库:TEAMPLAYERCONTRACT

TEAM(teamID, name)
PLAYER(playerID, name)
CONTRACT(contractID, playerID, teamID, dateOfSigning, expirationDate)

在这个数据库中,我想要一个玩家不能同时拥有多个合同的约束。 我提到我希望过期的合同仍在我的数据库中注册。

例如:

CONTRACT(1,1,1, 01/01/2000, 01/01/2005)
CONTRACT(1,1,1, 01/01/2001, 01/01/2003)

所以,我的玩家的合约来自 01/01/2000至01/01/2005 ,另一份合约来自 01/01/2001至01/01/2003。这是不可能的。

3 个答案:

答案 0 :(得分:1)

这不能通过声明性约束直接强制执行。不幸的是,目前的DBMS不提供“独特范围”约束(至少据我所知)。

您有两种选择:

  • 通过业务逻辑(通常是触发器或存储过程)强制执行此操作。但要注意并发性(您可能需要使用锁定来避免混淆相关日期的并发事务之间的竞争条件)。
  • 或减少时间的粒度。例如,如果您将年份除以月份,并声明给定合约“占用”哪个月(通过每个月分别划分),那么您可以轻松(并且声明性地)强制执行相同的任何重复项一个月。

注意:后者可以更精细,例如天,但这将在数据库中产生更多行并导致闰年等问题 - 您需要为您的特定情况找到合适的平衡。

答案 1 :(得分:1)

当且仅当一个玩家在另一个玩家完成后开始时,两个不同的合约不会重叠。因此,当且仅当NOT(一个在另一个完成之后开始)时它们重叠。您想要的约束是没有contractID对的行满足它们重叠的条件:

    CONTRACT(c1.contractID, playerID, c1.teamID, c1.dateOfSigning, c1.expirationDate)
AND CONTRACT(c2.contractID, playerID, c2.teamID, c2.dateOfSigning, c2.expirationDate)
AND c1.contractID <> c2.contractID
AND NOT(c1.dateOfSigning > c2.expirationDate
    OR c2.dateOfSigning > c1.expirationDate)

这意味着以下一组行为空:

SELECT c1.contractID, c2.contractID
FROM CONTRACT c1
JOIN CONTRACT c2
ON c1.playerID = c2.playerID
AND c1.contractID <> c2.contractID
WHERE NOT(c1.dateOfSigning > c2.expirationDate
    OR c2.dateOfSigning > c1.expirationDate)

如果DBMS在CHECK中支持SELECT,那么你可以有一个CONTRACT约束:

CHECK (SELECT CASE
        WHEN EXISTS (SELECT 1 FROM (...))
        THEN 1
        ELSE 0 END)

但它们并非如此,您必须在触发器或存储过程中测试此SELECT。 (如另一个答案所述。)DBMSes不支持任意约束检查。

您可以通过将过期合同保留在与活动合同不同的表中来减少计算。 (它们不会与新的重叠。)但我刚刚使用了你提供的表格。

答案 2 :(得分:0)

如果在合约表中,您使playerid成为唯一,并添加另一个表来保存合同历史记录。然后玩家只能拥有一个联系人但可能拥有多个联系人。

在业务层中,您需要确定要插入的合同是否应覆盖当前合同。如果是,请归档并从合同表中删除当前合同并插入新合同。