强制执行1:1和1:使用复合主键

时间:2016-03-31 12:54:48

标签: sql sql-server database tsql data-warehouse

我有一张名为" Accounts" composite primary key包含2列:Account_keyAccount_Start_date,数据类型为int,另一个非关键列为Accountnumber(bigint).

Account_key应该有一个或多个Accountnumber(bigint)而不是相反意味着 1或许多帐号只能有1个Account_key 。 如果您尝试插入相同的Account_key和相同的Account_Start_date,那么primary key constraint当然会停止这一点,因为它们是主键。

但是,如果您插入现有的Account_key与不同的现有Account_Start_date,那么您可以根据需要插入随机的Accountnumber,而不会抱怨任何约束,突然您在Account_key和Accountnumber之间有多对多的关系,我们不想那样

我试过很多约束而没有任何运气。我只是不知道我在这里做错了所以请继续帮我解决这个问题,谢谢! (注意:我不认为更改复合主键是一个选项,因为我们将松开缓慢变化的维度日期功能)

还有另一个表(案例),其中1' Account_Key'只能与1' AccountNumber'相关联意思是1..1关系,除了它们之间应该有1..1之间的关系外,其他所有事情都是相同的。 独特的索引至少对我不起作用,只要考虑我是否想要更改Accounts表或者设置一个触发器甚至是一个索引,这样它就会成为' Account_Key'之间的1..1关系。和' AccountNumber',?

3 个答案:

答案 0 :(得分:1)

如果这是OLTP表,解决方案是将数据正确地规范化为两个表,但这是一个DW表,因此将它全部放在一个表中是有意义的。

在这种情况下,您应该添加FOR / AFTER触发器ON INSERT, UPDATE,对inserted伪表进行查询。查询可以是一个简单的COUNT(DISTINCT Account_Key),加入主表(仅过滤添加/更新的AccountNumber值),在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;

对于"其他" 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;

答案 1 :(得分:0)

如果我理解正确,你想要:

  1. 任何给定的AccountNumber只能与一个AccountKey
  2. 相关联
  3. 任何给定的AccountKey都可以与多个AccountNumbers相关联
  4. 如果这是正确的,您可以使用调用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)
    

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

答案 2 :(得分:0)

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

将Account_Number设为主键。

请注意,您无法两次添加Account_Number,因此无法将其链接到此表中的两个不同的Account_Keys。

现在,您在Account_Number和Account_Key上添加了一个额外的唯一约束。在此表中,您可以输入所有帐号及其对应的密钥。

最后,在Accounts表上,在Account_Key和Account_Number列上定义一个外键,引用AccountKeyNumbers表中的唯一约束。

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