Ich habe das folgende Schema (Namen geändert), das ich nicht ändern kann:
CREATE TABLE MyTable (
Id INT NOT NULL PRIMARY KEY,
ParentId INT NOT NULL
);
ALTER TABLE MyTable ADD FOREIGN KEY (ParentId) REFERENCES MyTable(Id);
Das heißt, jeder Datensatz ist einem anderen Datensatz untergeordnet. Wenn der Wert eines Datensatzes ParentId
gleich dem Wert seines ist Id
, wird der Datensatz als Stammknoten betrachtet.
Ich möchte eine Abfrage ausführen, die alle Zirkelverweise findet. Zum Beispiel mit den Daten
INSERT INTO MyTable (Id, ParentId) VALUES
(0, 0),
(1, 0),
(2, 4),
(3, 2),
(4, 3);
Die Abfrage sollte zurückkehren
Id | Cycle
2 | 2 < 4 < 3 < 2
3 | 3 < 2 < 4 < 3
4 | 4 < 3 < 2 < 4
Ich habe die folgende Abfrage für SQL Server 2008 R2 geschrieben und frage mich, ob diese Abfrage verbessert werden kann:
IF OBJECT_ID(N'tempdb..#Results') IS NOT NULL DROP TABLE #Results;
CREATE TABLE #Results (Id INT, HasParentalCycle BIT, Cycle VARCHAR(MAX));
DECLARE @i INT,
@j INT,
@flag BIT,
@isRoot BIT,
@ids VARCHAR(MAX);
DECLARE MyCursor CURSOR FAST_FORWARD FOR
SELECT Id
FROM MyTable;
OPEN MyCursor;
FETCH NEXT FROM MyCursor INTO @i;
WHILE @@FETCH_STATUS = 0
BEGIN
IF OBJECT_ID(N'tempdb..#Parents') IS NOT NULL DROP TABLE #Parents;
CREATE TABLE #Parents (Id INT);
SET @ids = NULL;
SET @isRoot = 0;
SET @flag = 0;
SET @j = @i;
INSERT INTO #Parents (Id) VALUES (@j);
WHILE (1=1)
BEGIN
SELECT
@j = ParentId,
@isRoot = CASE WHEN ParentId = Id THEN 1 ELSE 0 END
FROM MyTable
WHERE Id = @j;
IF (@isRoot = 1)
BEGIN
SET @flag = 0;
BREAK;
END
IF EXISTS (SELECT 1 FROM #Parents WHERE Id = @j)
BEGIN
INSERT INTO #Parents (Id) VALUES (@j);
SET @flag = 1;
SELECT @ids = COALESCE(@ids + ' < ', '') + CAST(Id AS VARCHAR) FROM #Parents;
BREAK;
END
ELSE
BEGIN
INSERT INTO #Parents (Id) VALUES (@j);
END
END
INSERT INTO #Results (Id, HasParentalCycle, Cycle) VALUES (@i, @flag, @ids);
FETCH NEXT FROM MyCursor INTO @i;
END
CLOSE MyCursor;
DEALLOCATE MyCursor;
SELECT Id, Cycle
FROM #Results
WHERE HasParentalCycle = 1;
sql-server
sql-server-2008-r2
foreign-key
cubetwo1729
quelle
quelle
0 > 0
sollte nicht als Zyklus angesehen werden?ParentId
Wert gleich seinemId
ist. In diesem Szenario handelt es sich also nicht um einen Zyklus.Antworten:
Dies erfordert einen rekursiven CTE:
Sehen Sie es hier in Aktion: SQL Fiddle
Aktualisieren:
Entfernung hinzugefügt, um alle Selbstzyklen ausschließen zu können (siehe Kommentar von ypercube):
SQL-Geige
Welches Sie verwenden sollten, hängt von Ihrer Anforderung ab.
quelle
6 > 6
lange es nicht ist0 > 0
.WHERE Id <> ParentId
im ersten Teil des CTE hinzufügen .AND C.ParentId <> C.Id
ist nicht genug. Führt ein Pfad in einen längeren Kreis (A->B, B->C, C->B
), erhalten Sie immer noch eine unendliche Rekursion für die Erstellung der Pfade, die mit beginnenA
. Sie müssten wirklich den gesamten Pfad überprüfen.quelle