যখন কোনও সারণী নিজেই রেফারেন্স করে তখন সমস্ত বৃত্তাকার রেফারেন্স খুঁজে পাওয়া একটি কোয়েরি কীভাবে লিখবেন?


26

আমার নিম্নলিখিত স্কিমা (নাম পরিবর্তন হয়েছে), যা আমি পরিবর্তন করতে পারি না:

CREATE TABLE MyTable (
    Id INT NOT NULL PRIMARY KEY,
    ParentId INT NOT NULL
);

ALTER TABLE MyTable ADD FOREIGN KEY (ParentId) REFERENCES MyTable(Id);

অর্থাৎ, প্রতিটি রেকর্ড অন্য রেকর্ডের শিশু। যদি কোনও রেকর্ড এর ParentIdসমান হয় Idতবে রেকর্ডটিকে মূল নোড হিসাবে বিবেচনা করা হয়।

আমি ক্যোয়ারি চালাতে চাই যা সমস্ত বিজ্ঞপ্তি রেফারেন্সগুলি খুঁজে পাবে। উদাহরণস্বরূপ, ডেটা সহ

INSERT INTO MyTable (Id, ParentId) VALUES
    (0, 0),
    (1, 0),
    (2, 4),
    (3, 2),
    (4, 3);

কোয়েরিটি ফিরে আসা উচিত

Id | Cycle
2  | 2 < 4 < 3 < 2
3  | 3 < 2 < 4 < 3
4  | 4 < 3 < 2 < 4

আমি এসকিউএল সার্ভার ২০০৮ আর 2 এর জন্য নিম্নলিখিত কোয়েরিটি লিখেছি এবং আমি এই প্রশ্নটি আরও উন্নত করা যায় কিনা তা নিয়ে ভাবছি:

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;

0 > 0একটি চক্র বিবেচনা করা উচিত?
ypercubeᵀᴹ

1
না, 0 একটি মূল নোড, কারণ এটি এর ParentIdসমান Id, তাই এটি এই দৃশ্যের জন্য একটি চক্র নয়।
cubetwo1729

উত্তর:


30

এটি পুনরাবৃত্ত সিটিইয়ের জন্য কল করে:

WITH FindRoot AS
(
    SELECT Id,ParentId, CAST(Id AS NVARCHAR(MAX)) Path
    FROM dbo.MyTable

    UNION ALL

    SELECT C.Id, P.ParentId, C.Path + N' > ' + CAST(P.Id AS NVARCHAR(MAX))
    FROM dbo.MyTable P
    JOIN FindRoot C
    ON C.ParentId = P.Id AND P.ParentId <> P.Id AND C.ParentId <> C.Id
 )
SELECT *
FROM FindRoot R
WHERE R.Id = R.ParentId 
  AND R.ParentId <> 0;

এটি এখানে কর্মে দেখুন: এসকিউএল ফিডল


হালনাগাদ:

সমস্ত স্বচক্রকে বাদ দিতে সক্ষম করার জন্য দূরত্ব যুক্ত করা হয়েছে (ইপারকিউবের মন্তব্য দেখুন):

WITH FindRoot AS
(
    SELECT Id,ParentId, CAST(Id AS NVARCHAR(MAX)) Path, 0 Distance
    FROM dbo.MyTable

    UNION ALL

    SELECT C.Id, P.ParentId, C.Path + N' > ' + CAST(P.Id AS NVARCHAR(MAX)), C.Distance + 1
    FROM dbo.MyTable P
    JOIN FindRoot C
    ON C.ParentId = P.Id AND P.ParentId <> P.Id AND C.ParentId <> C.Id
 )
SELECT *
FROM FindRoot R
WHERE R.Id = R.ParentId 
  AND R.ParentId <> 0
  AND R.Distance > 0;

এসকিউএল ফিডল

আপনার কোনটি ব্যবহার করা উচিত তা আপনার প্রয়োজনের উপর নির্ভর করে।


এটি সংশোধন করা উচিত। বর্তমানে এটি 1-চক্রও দেখায় 6 > 6, যেমন এটি দীর্ঘ নয় 0 > 0
ypercubeᵀᴹ

আমি ওপি বুঝতে পেরেছিলাম যে কেবল আসল রুট নোডস স্ব চক্র বাদ দেওয়া উচিত। তবে আপনি যে আর.পাথকে চূড়ান্ত ক্ষেত্রে '%>%' এর মতো পরীক্ষা করে এই প্রয়োজনীয়তাটি সহজেই যুক্ত করতে পারেন যেখানে ক্লজ। অথবা আপনি পুনরাবৃত্ত সিটিই এর ভিতরে একটি চক্র দৈর্ঘ্য গণনা কলাম যুক্ত করতে পারেন।
সেবাস্তিয়ান মেইন

2
আপনি কেবল WHERE Id <> ParentIdসিটিইর প্রথম অংশে যুক্ত করতে পারেন ।
ypercubeᵀᴹ

AND C.ParentId <> C.Idযথেষ্ট না. এটি একটি পাথ দীর্ঘতর বৃত্তের দিকে নিয়ে যায় ( A->B, B->C, C->B), আপনি এখনও শুরু হওয়া পথগুলি নির্মাণের জন্য অসীম পুনরাবৃত্তি পেতে চান A। আপনাকে সত্যই পুরো পথটি পরীক্ষা করতে হবে।
বার্গি

2
SELECT RC.CONSTRAINT_NAME FK_Name
, KF.TABLE_SCHEMA FK_Schema
, KF.TABLE_NAME FK_Table
, KF.COLUMN_NAME FK_Column
, RC.UNIQUE_CONSTRAINT_NAME PK_Name
, KP.TABLE_SCHEMA PK_Schema
, KP.TABLE_NAME PK_Table
, KP.COLUMN_NAME PK_Column
, RC.MATCH_OPTION MatchOption
, RC.UPDATE_RULE UpdateRule
, RC.DELETE_RULE DeleteRule
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KF ON RC.CONSTRAINT_NAME = KF.CONSTRAINT_NAME
JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KP ON RC.UNIQUE_CONSTRAINT_NAME = KP.CONSTRAINT_NAME
WHERE KF.TABLE_NAME = KP.TABLE_NAME

1
এবং কিভাবে এই কাজ করে? এটি সাধারণত এমন ব্যাখ্যা যা একটি ভাল উত্তর দেয়। কোড-কেবলমাত্র পোস্টগুলি এখানে (সাধারণত, কমপক্ষে) ভ্রূণ্য হয়।
dezso

2
দেখে মনে হচ্ছে এটি একইরকম তবে ভিন্ন প্রশ্নের উত্তর দেয়।
ypercubeᵀᴹ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.