Per verificare la presenza di riferimenti circolari ho utilizzato un trigger e un CTE ricorsivo:
CREATE TRIGGER trgIU_X_CheckCircularReferences
ON dbo.X
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE @Results TABLE ([Exists] BIT);
WITH CteHierarchy
AS
(
SELECT x.A, x.B, X.C, 1 AS [Type]
FROM inserted i
JOIN X x ON i.A = x.A AND i.C = x.B
UNION ALL
SELECT x.A, x.B, X.C, 2 AS [Type]
FROM CteHierarchy i
JOIN X x ON i.A = x.A AND i.C = x.B
WHERE NOT EXISTS
(
SELECT *
FROM inserted a
WHERE a.A = x.A AND a.B = x.B
)
)
INSERT @Results ([Exists])
SELECT TOP(1) 1
FROM CteHierarchy h
JOIN X x ON h.A = x.A AND h.C = x.B
OPTION(MAXRECURSION 1000);
IF EXISTS(SELECT * FROM @Results)
BEGIN
ROLLBACK;
RAISERROR('Circular references detected', 16, 1);
END
END
GO
Ora possiamo eseguire alcuni test:
--Test 1 - OK
PRINT '*****Test 1 - OK*****';
SELECT * FROM X;
BEGIN TRANSACTION;
UPDATE X
SET C = 'B1'
WHERE B = 'B4';
SELECT * FROM X;
--This transaction can be commited without problems
--but I will cancel all modification so we can run the second test
ROLLBACK TRANSACTION;
PRINT '*****End of test 1*****';
GO
--Test 2 - NOT OK
PRINT '*****Test 2 - NOT OK*****';
SELECT * FROM X;
BEGIN TRANSACTION;
UPDATE X
SET C = 'B1'
WHERE B = 'B1';
--Useless in this case (test 2 & test 3)
--Read section [If a ROLLBACK TRANSACTION is issued in a trigger] from http://msdn.microsoft.com/en-us/library/ms181299.aspx
SELECT * FROM X;
--Useless
ROLLBACK TRANSACTION;
--Useless
PRINT '*****End of test 2*****';
GO
PRINT '*****Test 3 - NOT OK*****';
SELECT * FROM X;
BEGIN TRANSACTION;
UPDATE X
SET C = 'B4'
WHERE B = 'B1';
GO
Risultati:
*****Test 1 - OK*****
(4 row(s) affected)
(0 row(s) affected)
(1 row(s) affected)
(4 row(s) affected)
*****End of test 1*****
*****Test 2 - NOT OK*****
(4 row(s) affected)
(1 row(s) affected)
Msg 50000, Level 16, State 1, Procedure trgIU_X_CheckCircularReferences, Line 34
Circular references detected
Msg 3609, Level 16, State 1, Line 8
The transaction ended in the trigger. The batch has been aborted.
*****Test 3 - NOT OK*****
(4 row(s) affected)
(1 row(s) affected)
Msg 50000, Level 16, State 1, Procedure trgIU_X_CheckCircularReferences, Line 34
Circular references detected
Msg 3609, Level 16, State 1, Line 7
The transaction ended in the trigger. The batch has been aborted.
Per il secondo test, puoi vedere come questo attivatore è stato annullato (ROLLBACK TRANSACTION
) la transazione e, dopo UPDATE, nulla è stato eseguito (nel batch corrente ).