Reputation: 115
I have a table called "Accounts
" with a composite primary key
consisting of 2 columns: Account_key
and Account_Start_date
both with the datatype int
and another non key column named Accountnumber(bigint).
Account_key should have one or many Accountnumber(bigint)
and not the other way around meaning 1 or many Accountnumber can only have 1 Account_key.
If you try to insert same Account_key and same Account_Start_date then the primary key constraint
is stopping this of course because they are together primary key.
However if you insert existing Account_key with different non existing Account_Start_date then you could insert a random Accountnumber as you wish without any constraint complaining about it, and suddenly you have rows with many to many relations between Account_key and Accountnumber and we dont want that.
I have tried with a lot of constrains without any luck. I just don't know what I am doing wrong here so please go ahead and help me on this, thanks! (Note: I dont think changing the composite primary key is an option because then we will loose the slowly changing dimension date functionality)
There is another table (case) where 1 'Account_Key' can only be related to 1 'AccountNumber' meaning 1..1 relation, all other things is the same except that there should be 1..1 relation between them.
Unique index havent work for me at least, just consider if I wanted to change Accounts
table or put a trigger or even a Index so it will be 1..1 relation between 'Account_Key' and 'AccountNumber', ?
Upvotes: 1
Views: 144
Reputation: 48874
If this were an OLTP table the solution would be to properly normalize the data into two tables, but this is a DW table so it makes sense to have it all in one table.
In this case, you should add a FOR
/ AFTER
Trigger ON INSERT, UPDATE
that does a query against the inserted
pseudo-table. The query can be a simple COUNT(DISTINCT Account_Key)
, joining back to the main table (to filter on just the AccountNumber
values being added/updated), doing a GROUP BY
on AccountNumber
and then HAVING COUNT(DISTINCT Account_Key) > 1
. Wrap that query in an IF EXISTS
and if a row is returned, then execute a ROLLBACK
to cancel the DML operation, a RAISERROR
to send the error message about why the operation is being cancelled, and then 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;
For the "other" table where the relationship between Account_Key
and AccountNumber
is 1:1, you might could try doing something like:
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;
Upvotes: 1
Reputation: 31785
If I understand you correctly, you want:
If this is correct, you can achieve this with a CHECK CONSTRAINT
that calls a UDF.
EDIT:
Psuedo-logic for the CHECK CONSTRAINT could be:
IF EXISTS anotherRow
WHERE theOtherAccountNumber = thisAccountNumber
AND theOtherAccountKey <> thisAccountKey
THEN False (do not allow this row to be inserted)
ELSE True (allow the insertion)
I would put this logic in a UDF that returns true or false to make the CHECK constraint simpler.
Upvotes: 0
Reputation: 383
Create an extra table AccountKeyNumbers (name is your choice of course) with columns Account_Key and Account_Number.
Make Account_Number the primary key.
Note that you can't add an Account_Number twice and therefore, can't link it to two different Account_Keys in this table.
Now, you add an extra unique constraint on Account_Number plus Account_Key. In this table you put all the account numbers and their corresponding keys.
Finally, you define a foreign key on your Accounts table, on the columns Account_Key plus Account_Number, referencing the unique constraint in the AccountKeyNumbers table.
You've now ensured that only valid key/number combinations can be inserted into Accounts and no two AccountKeys can have the same number. We needed the extra unique constraint, which doesn't contribute to the data integrity of the AccountKeyNumbers table, just to enable creating a foreign key, which must point to a primary or unique constraint.
Upvotes: 0