Please Note: I'm a Software Developer with limited knowledge of Database Programming/Administration.
I'm trying to build a structure where companies can have parent-child relationships with one another. The basic idea being that you might have different branches of a company operating in different parts of the world which share some data but not all data (Mailing Address, and Local Contacts for instance).
A striped down version of the table would look something like this:
CREATE TABLE COMPANY (
COMPANY_ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
PARENT_COMPANY INT FOREIGN KEY REFERENCES COMPANY(COMPANY_ID),
NAME NVARCHAR(256)
);
The problem I'm having is that this structure allows cyclical relationships where a parent company could become the child of one of its descendants. The sql below would cause such a situation to occur.
INTO COMPANY
(COMPANY_ID, PARENT_COMPANY, NAME)
VALUES
(1, null, 'Company1'),
(2, 1, 'Company2'),
(3, 2, 'Company3');
UPDATE COMPANY
SET PARENT_COMPANY = 3
WHERE COMPANY_ID = 1;
Because this kind of relationship could cause infinite loops, I want to prevent the situation from ever occurring.
The best idea I could come up with was run a trigger on the
COMPANY
table, that would check to make sure any value updated in the PARENT_COMPANY
column didn't cause a cyclical relationship. However Sql Server doesn't have a BEFORE UPDATE
trigger; only AFTER
and INSTEAD OF
triggers, both of which run after the table has already been updated. This means the cyclical relationship would already be created before I got a chance to check for it.
At this point I could potentially rebuild the "before" version of the table from the triggers DELETED
temporary table, and the Company Table itself; then search that table for a cyclical relationship; but that seems very cumbersome and inefficient.
Is there any other way I could check for cyclical relationships in a self referencing Sql Server structure.
PS. I was planning on using something like this to search for a cyclical relationship in the trigger:
DECLARE @CompanyId int = 1 -- ID of company thats been changed
;WITH cte AS
(
SELECT a.COMPANY_ID, a.PARENT_COMPANY, a.NAME
FROM COMPANY a
WHERE COMPANY_ID = @CompanyId
UNION ALL
SELECT a.COMPANY_ID, a.PARENT_COMPANY, a.NAME
FROM COMPANY a JOIN cte c ON a.PARENT_COMPANY = c.COMPANY_ID
)
SELECT COMPANY_ID, PARENT_COMPANY, NAME
FROM cte
ORDER BY NAME