Saya memiliki skema berikut (nama diubah), yang tidak dapat saya ubah:
CREATE TABLE MyTable (
Id INT NOT NULL PRIMARY KEY,
ParentId INT NOT NULL
);
ALTER TABLE MyTable ADD FOREIGN KEY (ParentId) REFERENCES MyTable(Id);
Artinya, setiap catatan adalah anak dari catatan lain. Jika record ParentId
sama dengan nya Id
, maka record dianggap sebagai root node.
Saya ingin menjalankan kueri yang akan menemukan semua referensi melingkar. Misalnya dengan data
INSERT INTO MyTable (Id, ParentId) VALUES
(0, 0),
(1, 0),
(2, 4),
(3, 2),
(4, 3);
kueri harus kembali
Id | Cycle
2 | 2 < 4 < 3 < 2
3 | 3 < 2 < 4 < 3
4 | 4 < 3 < 2 < 4
Saya menulis kueri berikut untuk SQL Server 2008 R2, dan saya ingin tahu apakah kueri ini dapat diperbaiki:
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
sumber
sumber
0 > 0
tidak harus dianggap siklus?ParentId
sama dengan simpulnyaId
, jadi ini bukan siklus untuk skenario ini.Jawaban:
Ini panggilan untuk CTE rekursif:
Lihat beraksi di sini: SQL Fiddle
Memperbarui:
Menambahkan jarak untuk dapat mengecualikan semua siklus diri (lihat komentar ypercube):
SQL Fiddle
Yang mana yang harus Anda gunakan tergantung pada kebutuhan Anda.
sumber
6 > 6
, asalkan tidak0 > 0
.WHERE Id <> ParentId
di bagian pertama CTE.AND C.ParentId <> C.Id
tidak cukup. Jika sebuah jalur mengarah ke lingkaran yang lebih panjang (A->B, B->C, C->B
), Anda masih akan mendapatkan rekursi tak terbatas untuk membangun jalur yang dimulaiA
. Anda benar-benar perlu memeriksa seluruh jalur.sumber