使用复合主键在非规范化仓库 table 中强制执行 1:1 和 1:Many 基数

Enforcing 1:1 and 1:Many cardinality in denormalized warehouse table with composite Primary Key

我有一个名为“Accounts”的 table,其中 composite primary key 由 2 列组成:Account_keyAccount_Start_date 都具有数据类型 int 和另一个名为 Accountnumber(bigint).

的非键列

Account_key 应该有一个或多个 Accountnumber(bigint) 而不是相反的意思 1 or many Accountnumber can only have 1 Account_key . 如果您尝试插入相同的 Account_key 和相同的 Account_Start_date ,那么 primary key constraint 当然会停止这样做,因为它们是主键。

但是,如果您插入现有的 Account_key 和不同的不存在的 Account_Start_date,那么您可以随心所欲地插入一个随机帐号,而不会受到任何限制,突然间您有了Account_key 和 Accountnumber 之间具有多对多关系的行,我们不想要 .

我尝试了很多限制条件,但都没有成功。我只是不知道我在这里做错了什么所以请继续帮助我,谢谢! (注意:我不认为更改复合主键是一个选项,因为那样我们将失去缓慢变化的维度日期功能)

还有一个table(案例)1 'Account_Key'只能与1 'AccountNumber'相关,意思是1..1的关系,除了有它们之间应该是 1..1 关系。 唯一索引至少对我没有用,只要考虑我是否想更改 Accounts table 或放置触发器甚至索引,这样 'Account_Key' 和之间的关系将是 1..1 'AccountNumber', ?

如果我没理解错的话,你想要:

  1. 任何给定的 AccountNumber 只能与一个 AccountKey 关联
  2. 任何给定的 AccountKey 都可以与多个 AccountNumbers 相关联

如果这是正确的,您可以使用调用 UDF 的 CHECK CONSTRAINT 来实现。

编辑:

CHECK CONSTRAINT 的伪逻辑可以是:

IF EXISTS anotherRow 
WHERE theOtherAccountNumber = thisAccountNumber 
AND theOtherAccountKey <> thisAccountKey
THEN False (do not allow this row to be inserted)
ELSE True (allow the insertion)

我会将此逻辑放在 returns true 或 false 的 UDF 中,以使 CHECK 约束更简单。

使用列 Account_Key 和 Account_Number 创建一个额外的 table AccountKeyNumbers(名称当然是您的选择)。

将Account_Number设为主键。

请注意,您不能将 Account_Number 添加两次,因此也不能 link 将其添加到此 table 中的两个不同的 Account_Key。

现在,您在 Account_Number 加上 Account_Key 上添加了一个额外的唯一约束。在此 table 中,您放入所有帐号及其相应的密钥。

最后,您在帐户 table 的列 Account_Key 加上 Account_Number 上定义外键,引用 AccountKeyNumbers table 中的唯一约束。

您现在已确保只能将有效的 key/number 组合插入到帐户中,并且两个帐户密钥的编号不能相同。我们需要额外的唯一约束,它不会影响 AccountKeyNumbers table 的数据完整性,只是为了能够创建一个必须指向主约束或唯一约束的外键。

如果这是一个 OLTP table,解决方案是将数据正确规范化为两个 table,但这是一个 DW table,因此拥有它是有意义的一应俱全 table.

在这种情况下,您应该添加一个 FOR / AFTER 触发器 ON INSERT, UPDATE 来针对 inserted 伪 table 执行查询。查询可以是一个简单的 COUNT(DISTINCT Account_Key),连接回主 table(仅过滤 AccountNumber 值为 added/updated),在 GROUP BY AccountNumber 然后 HAVING COUNT(DISTINCT Account_Key) > 1。将该查询包装在 IF EXISTS 中,如果返回一行,则执行 ROLLBACK 取消 DML 操作,执行 RAISERROR 发送有关取消操作原因的错误消息,然后 RETURN.

CREATE TRIGGER dbo.TR_TableName_PreventDuplicateAccountNumbers
ON dbo.TableName
AFTER INSERT, UPDATE
AS
SET NOCOUNT ON;

IF (EXISTS(
   SELECT COUNT(DISTINCT tab.Account_Key)
   FROM   dbo.TableName tab
   INNER JOIN INSERTED ins
                    ON ins.AccountNumber = tab.AccountNumber
   GROUP BY  tab.AccountNumber
   HAVING COUNT(DISTINCT tab.Account_Key) > 1
   ))
BEGIN
  ROLLBACK;
  RAISERROR(N'AccountNumber cannot be associated with more than 1 Account_Key', 16, 1);
  RETURN;
END;

对于 "other" table,其中 Account_KeyAccountNumber 之间的关系是 1:1,您可以尝试执行以下操作:

DECLARE @Found BIT = 0;

;WITH cte AS
(
  SELECT DISTINCT tab.Account_Key, tab.AccountNumber
  FROM   dbo.TableName tab
  INNER JOIN INSERTED ins
          ON ins.Account_Key = tab.Account_Key
          OR ins.AccountNumber = tab.AccountNumber
), counts AS
(
  SELECT c.[Account_Key],
         c.[AccountNumber],
         ROW_NUMBER() OVER (PARTITION BY c.[Account_Key
                            ORDER BY c.[Account_Key, c.[AccountNumber]) AS [KeyCount],
         ROW_NUMBER() OVER (PARTITION BY c.[AccountNumber]
                            ORDER BY c.[AccountNumber], c.[Account_Key) AS [NumberCount]
  FROM cte c
)
SELECT @Found = 1
FROM   counts
WHERE  [KeyCount] > 1
OR     [NumberCount] > 1;

IF (@Found = 1)
BEGIN
   ROLLBACK;
   RAISERROR(N'AccountNumber cannot be associated with more than 1 Account_Key', 16, 1);
   RETURN;
END;