sql - 强制执行 1 :1 and 1:Many cardinality in denormalized warehouse table with composite Primary Key

标签 sql sql-server database tsql data-warehouse

我有一个名为“Accounts”的表,其复合主键由两列组成:Account_keyAccount_Start_date 都具有数据类型 int 和另一个名为 Accountnumber(bigint).

的非键列

Account_key 应该有一个或多个 Accountnumber(bigint) 而不是相反,这意味着 1 个或多个 Accountnumber 只能有 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”和“AccountNumber”之间的 1..1 关系, ?

最佳答案

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

在这种情况下,您应该添加一个 FOR/AFTER 触发器 ON INSERT, UPDATE 对插入的 执行查询 伪表。查询可以是一个简单的 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;

关于sql - 强制执行 1 :1 and 1:Many cardinality in denormalized warehouse table with composite Primary Key,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36334124/

相关文章:

php - 迁移后测试数据(HTML -> PHP -> SQL)

java - JOOQ SQL给定1个参数但预期为0

sql-server - 如何在 SQL Server 中定义 "complicated"计算列?

php - 如何以表单形式向我的数据库添加投票?

database - 是否每个开发人员一个数据库?

java - Symfony 执行访问数据库的 java 程序

c# - 触发及时事件的最佳方式

php - 如何使用 laravel 将表单中的数据发布到数据库中?

java - 数据库和服务器 UML

sql - 优化 postgresql 查询