sql - what - কোনও পুনর্নির্দেশিত গ্রাফের সমস্ত সংযুক্ত উপগ্রাফগুলি কীভাবে সন্ধান করবেন




what is sql (2)

এই স্ক্রিপ্টটি প্রয়োজন অনুযায়ী পরীক্ষার 1, 2 এবং 3 সেটগুলির আউটপুট তৈরি করে। স্ক্রিপ্টে মন্তব্য হিসাবে অ্যালগরিদম নোট।

সচেতন থাকা:

  • এই অ্যালগরিদম ইনপুট সেটটি ধ্বংস করে। স্ক্রিপ্টে ইনপুট সেটটি # টি #tree । সুতরাং এই স্ক্রিপ্টটি ব্যবহারের জন্য উত্সের ডেটা #tree সন্নিবেশ করা #tree
  • এই অ্যালগরিদম নোডগুলির জন্য NULL মানগুলির জন্য কাজ করে না। এই ঘাটতি থেকে ISNULL(source_col,CHAR(0)) ব্যবহার করে ISNULL(source_col,CHAR(0)) #tree প্রবেশ করার সময় CHAR(0) দিয়ে #tree মানগুলি প্রতিস্থাপন করুন। চূড়ান্ত ফলাফল থেকে নির্বাচন করার সময়, NULLIF(node,CHAR(0)) ব্যবহার করে CHAR(0) NULL সাথে প্রতিস্থাপন করুন।

মনে রাখবেন যে রিকার্সিভ সিটিই ব্যবহার করে উত্তরটি আরও মার্জিত যে এটি একক এসকিউএল বিবৃতি, তবে পুনরাবৃত্ত সিটিই ব্যবহার করে বড় ইনপুট সেটগুলির জন্য অস্বাভাবিক মৃত্যুর সময় দিতে পারে (সেই উত্তরের এই মন্তব্যটি দেখুন)। নীচে বর্ণিত সমাধানটি আরও সংশ্লেষিত অবস্থায় বৃহত্তর ইনপুট সেটের জন্য খুব দ্রুত চালানো উচিত।

SET NOCOUNT ON;

CREATE TABLE #tree(node_l CHAR(1),node_r CHAR(1));
CREATE NONCLUSTERED INDEX NIX_tree_node_l ON #tree(node_l)INCLUDE(node_r); -- covering indices to speed up lookup
CREATE NONCLUSTERED INDEX NIX_tree_node_r ON #tree(node_r)INCLUDE(node_l);
INSERT INTO #tree(node_l,node_r) VALUES
    ('a','c'),('b','f'),('a','g'),('c','h'),('b','j'),('d','f'),('e','k'),('i','i'),('l','h'); -- test set 1
    --('a','f'),('a','g'),(CHAR(0),'a'),('b','c'),('b','a'),('b','h'),('b','j'),('b',CHAR(0)),('b',CHAR(0)),('b','g'),('c','k'),('c','b'),('d','l'),('d','f'),('d','g'),('d','m'),('d','a'),('d',CHAR(0)),('d','a'),('e','c'),('e','b'),('e',CHAR(0)); -- test set 2
    --('a','a'),('b','b'),('c','a'),('c','b'),('c','c'); -- test set 3 

CREATE TABLE #sets(node CHAR(1) PRIMARY KEY,group_id INT); -- nodes with group id assigned
CREATE TABLE #visitor_queue(node CHAR(1)); -- contains nodes to visit
CREATE TABLE #visited_nodes(node CHAR(1) PRIMARY KEY CLUSTERED WITH(IGNORE_DUP_KEY=ON)); -- nodes visited for nodes on the queue; ignore duplicate nodes when inserted
CREATE TABLE #visitor_ctx(node_l CHAR(1),node_r CHAR(1)); -- context table, contains deleted nodes as they are visited from #tree

DECLARE @last_created_group_id INT=0;

-- Notes:
-- 1. This algorithm is destructive in its input set, ie #tree will be empty at the end of this procedure
-- 2. This algorithm does not accept NULL values. Populate #tree with CHAR(0) for NULL values (using ISNULL(source_col,CHAR(0)), or COALESCE(source_col,CHAR(0)))
-- 3. When selecting from #sets, to regain the original NULL values use NULLIF(node,CHAR(0))
WHILE EXISTS(SELECT*FROM #tree)
BEGIN
    TRUNCATE TABLE #visited_nodes;
    TRUNCATE TABLE #visitor_ctx;

    -- push first nodes onto the queue (via #visitor_ctx -> #visitor_queue)
    DELETE TOP (1) t
    OUTPUT deleted.node_l,deleted.node_r INTO #visitor_ctx(node_l,node_r)
    FROM #tree AS t;

    INSERT INTO #visitor_queue(node) SELECT node_l FROM #visitor_ctx UNION SELECT node_r FROM #visitor_ctx; -- UNION to filter when node_l equals node_r
    INSERT INTO #visited_nodes(node) SELECT node FROM #visitor_queue; -- keep track of nodes visited

    -- work down the queue by visiting linked nodes in #tree; nodes are deleted as they are visited
    WHILE EXISTS(SELECT*FROM #visitor_queue)
    BEGIN
        TRUNCATE TABLE #visitor_ctx;

        -- pop_front for node on the stack (via #visitor_ctx -> @node)
        DELETE TOP (1) s
        OUTPUT deleted.node INTO #visitor_ctx(node_l)
        FROM #visitor_queue AS s;

        DECLARE @node CHAR(1)=(SELECT node_l FROM #visitor_ctx); 
        TRUNCATE TABLE #visitor_ctx;

        -- visit nodes in #tree where node_l or node_r equal target @node; 
        -- delete visited nodes from #tree, output to #visitor_ctx
        DELETE t
        OUTPUT deleted.node_l,deleted.node_r INTO #visitor_ctx(node_l,node_r)
        FROM #tree AS t
        WHERE t.node_l[email protected]node OR t.node_r[email protected]node;

        -- insert visited nodes in the queue that haven't been visited before
        INSERT INTO #visitor_queue(node) 
        (SELECT node_l FROM #visitor_ctx UNION SELECT node_r FROM #visitor_ctx) EXCEPT (SELECT node FROM #visited_nodes);

        -- keep track of visited nodes (duplicates are ignored by the IGNORE_DUP_KEY option for the PK)
        INSERT INTO #visited_nodes(node)
        SELECT node_l FROM #visitor_ctx UNION SELECT node_r FROM #visitor_ctx;
    END

    SET @last_created_group_id+=1; -- create new group id

    -- insert group into #sets
    INSERT INTO #sets(group_id,node)
    SELECT group_id[email protected]last_created_group_id,node 
    FROM #visited_nodes;
END

SELECT node=NULLIF(node,CHAR(0)),group_id FROM #sets ORDER BY node; -- nodes with their assigned group id

SELECT g.group_id,m.members  -- groups with their members
FROM
    (SELECT DISTINCT group_id FROM #sets) AS g
    CROSS APPLY (
        SELECT members=STUFF((
                SELECT ','+ISNULL(CAST(NULLIF(si.node,CHAR(0)) AS VARCHAR(4)),'NULL')
                FROM #sets AS si 
                WHERE si.group_id=g.group_id
                FOR XML PATH('')
            ),1,1,'')
     ) AS m
ORDER BY g.group_id;

DROP TABLE #visitor_queue;
DROP TABLE #visited_nodes;
DROP TABLE #visitor_ctx;
DROP TABLE #sets;
DROP TABLE #tree;

1 সেট জন্য আউটপুট:

+------+----------+
| node | group_id |
+------+----------+
| a    |        1 |
| b    |        2 |
| c    |        1 |
| d    |        2 |
| e    |        4 |
| f    |        2 |
| g    |        1 |
| h    |        1 |
| i    |        3 |
| j    |        2 |
| k    |        4 |
| l    |        1 |
+------+----------+

2 সেট জন্য আউটপুট:

+------+----------+
| node | group_id |
+------+----------+
| NULL |        1 |
| a    |        1 |
| b    |        1 |
| c    |        1 |
| d    |        1 |
| e    |        1 |
| f    |        1 |
| g    |        1 |
| h    |        1 |
| j    |        1 |
| k    |        1 |
| l    |        1 |
| m    |        1 |
+------+----------+

3 সেট জন্য আউটপুট:

+------+----------+
| node | group_id |
+------+----------+
| a    |        1 |
| b    |        1 |
| c    |        1 |
+------+----------+

যে সমস্যার সমাধান করার জন্য আমি লড়াই করে যাচ্ছি তার জন্য আমার কিছু সহায়তা দরকার।

উদাহরণ সারণী:

ID |Identifier1 | Identifier2 
---------------------------------
1  |      a     | c         
2  |      b     | f         
3  |      a     | g         
4  |      c     | h        
5  |      b     | j         
6  |      d     | f         
7  |      e     | k  
8  |      i     |          
9  |      l     | h    

আমি দুটি কলামের মধ্যে একে অপরের সাথে সম্পর্কিত এবং পরিচয় গোষ্ঠীগুলি করতে চাই এবং একটি অনন্য গ্রুপ আইডি নির্ধারণ করতে চাই।

পছন্দসই আউটপুট:

Identifier | Gr_ID    |    Gr.Members                 
---------------------------------------------------
a       |      1      |   (a,c,g,h,l)  
b       |      2      |   (b,d,f,j)       
c       |      1      |   (a,c,g,h,l)  
d       |      2      |   (b,d,f,j)       
e       |      3      |   (e,k)                 
f       |      2      |   (b,d,f,j)       
g       |      1      |   (a,c,g,h,l)  
h       |      1      |   (a,c,g,h,l)  
j       |      2      |   (b,d,f,j)       
k       |      3      |   (e,k)                 
l       |      1      |   (a,c,g,h,l)  
i       |      4      |   (i)  

দ্রষ্টব্য: জুনিয়র মেম্বারস কলামটি প্রয়োজনীয় নয়, বেশিরভাগ ক্ষেত্রে পরিষ্কার দৃষ্টিতে ব্যবহার করা হয়।

সুতরাং একটি গোষ্ঠীর সংজ্ঞাটি হ'ল: একটি সারি একটি গ্রুপের অন্তর্ভুক্ত যদি এটি এই দলের অন্তত একটি সারি সহ কমপক্ষে একটি সনাক্তকারী ভাগ করে দেয়

তবে গ্রুপ আইডি প্রতিটি শনাক্তকারীকে নির্ধারণ করতে হবে (দুটি কলামের ইউনিয়ন দ্বারা নির্বাচিত) সারিটিতে নয়।

পছন্দসই আউটপুট দেওয়ার জন্য কীভাবে কোনও কোয়েরি তৈরি করবেন?

ধন্যবাদ.

আপডেট: নীচে তাদের প্রত্যাশিত আউটপুট সহ কিছু অতিরিক্ত নমুনা সেট রয়েছে।

প্রদত্ত টেবিল:

Identifier1 | Identifier2   
----------------------------
    a       |   f
    a       |   g
    a       |  NULL
    b       |   c
    b       |   a
    b       |   h
    b       |   j
    b       |  NULL
    b       |  NULL
    b       |   g
    c       |   k
    c       |   b
    d       |   l
    d       |   f
    d       |   g
    d       |   m
    d       |   a
    d       |  NULL
    d       |   a
    e       |   c
    e       |   b
    e       |  NULL

প্রত্যাশিত আউটপুট: সমস্ত রেকর্ডগুলি গ্রুপ আইডি = 1 সহ একই গ্রুপের হতে হবে।

প্রদত্ত সারণী:

Identifier1 | Identifier2
--------------------------
a           |   a
b           |   b
c           |   a
c           |   b
c           |   c

প্রত্যাশিত আউটপুট: রেকর্ডগুলি গ্রুপ আইডি = 1 সহ একই গ্রুপে থাকা উচিত।


এখানে একটি বৈকল্পিক যা কার্সার ব্যবহার করে না, তবে একটি একক পুনরাবৃত্ত কোয়েরি ব্যবহার করে।

মূলত, এটি ডেটাটিকে গ্রাফের প্রান্ত হিসাবে গণ্য করে এবং লুপটি সনাক্ত হওয়ার পরে বন্ধ হয়ে যায় এবং গ্রাফের সমস্ত প্রান্তকে পুনরাবৃত্তি করে। তারপরে এটি সমস্ত পাওয়া লুপগুলিকে গ্রুপগুলিতে রাখে এবং প্রতিটি গ্রুপকে একটি নম্বর দেয়।

এটি কীভাবে নীচে কাজ করে তার বিশদ বিবরণ দেখুন। আমি আপনাকে সিটিই-বাই-সিটিই কোয়েরি চালানোর পরামর্শ দিচ্ছি এবং এটি কী করে তা বুঝতে প্রতিটি মধ্যবর্তী ফলাফল পরীক্ষা করে দেখুন examine

নমুনা 1

DECLARE @T TABLE (ID int, Ident1 char(1), Ident2 char(1));
INSERT INTO @T (ID, Ident1, Ident2) VALUES
(1, 'a', 'a'),
(2, 'b', 'b'),
(3, 'c', 'a'),
(4, 'c', 'b'),
(5, 'c', 'c');

নমুনা 2

আমি জোড় মান সহ আরও একটি সারি যুক্ত করেছি যাতে আনকিয়ারযুক্ত মানগুলির সাথে একাধিক সারি রয়েছে।

DECLARE @T TABLE (ID int, Ident1 char(1), Ident2 char(1));
INSERT INTO @T (ID, Ident1, Ident2) VALUES
(1, 'a', 'a'),
(1, 'a', 'c'),
(2, 'b', 'f'),
(3, 'a', 'g'),
(4, 'c', 'h'),
(5, 'b', 'j'),
(6, 'd', 'f'),
(7, 'e', 'k'),
(8, 'i', NULL),
(88, 'z', 'z'),
(9, 'l', 'h');

নমুনা 3

DECLARE @T TABLE (ID int, Ident1 char(1), Ident2 char(1));
INSERT INTO @T (ID, Ident1, Ident2) VALUES
(1, 'a', 'f'),
(2, 'a', 'g'),
(3, 'a', NULL),
(4, 'b', 'c'),
(5, 'b', 'a'),
(6, 'b', 'h'),
(7, 'b', 'j'),
(8, 'b', NULL),
(9, 'b', NULL),
(10, 'b', 'g'),
(11, 'c', 'k'),
(12, 'c', 'b'),
(13, 'd', 'l'),
(14, 'd', 'f'),
(15, 'd', 'g'),
(16, 'd', 'm'),
(17, 'd', 'a'),
(18, 'd', NULL),
(19, 'd', 'a'),
(20, 'e', 'c'),
(21, 'e', 'b'),
(22, 'e', NULL);

প্রশ্ন

WITH
CTE_Idents
AS
(
    SELECT Ident1 AS Ident
    FROM @T

    UNION

    SELECT Ident2 AS Ident
    FROM @T
)
,CTE_Pairs
AS
(
    SELECT Ident1, Ident2
    FROM @T
    WHERE Ident1 <> Ident2

    UNION

    SELECT Ident2 AS Ident1, Ident1 AS Ident2
    FROM @T
    WHERE Ident1 <> Ident2
)
,CTE_Recursive
AS
(
    SELECT
        CAST(CTE_Idents.Ident AS varchar(8000)) AS AnchorIdent 
        , Ident1
        , Ident2
        , CAST(',' + Ident1 + ',' + Ident2 + ',' AS varchar(8000)) AS IdentPath
        , 1 AS Lvl
    FROM 
        CTE_Pairs
        INNER JOIN CTE_Idents ON CTE_Idents.Ident = CTE_Pairs.Ident1

    UNION ALL

    SELECT 
        CTE_Recursive.AnchorIdent 
        , CTE_Pairs.Ident1
        , CTE_Pairs.Ident2
        , CAST(CTE_Recursive.IdentPath + CTE_Pairs.Ident2 + ',' AS varchar(8000)) AS IdentPath
        , CTE_Recursive.Lvl + 1 AS Lvl
    FROM
        CTE_Pairs
        INNER JOIN CTE_Recursive ON CTE_Recursive.Ident2 = CTE_Pairs.Ident1
    WHERE
        CTE_Recursive.IdentPath NOT LIKE CAST('%,' + CTE_Pairs.Ident2 + ',%' AS varchar(8000))
)
,CTE_RecursionResult
AS
(
    SELECT AnchorIdent, Ident1, Ident2
    FROM CTE_Recursive
)
,CTE_CleanResult
AS
(
    SELECT AnchorIdent, Ident1 AS Ident
    FROM CTE_RecursionResult

    UNION

    SELECT AnchorIdent, Ident2 AS Ident
    FROM CTE_RecursionResult
)
SELECT
    CTE_Idents.Ident
    ,CASE WHEN CA_Data.XML_Value IS NULL 
    THEN CTE_Idents.Ident ELSE CA_Data.XML_Value END AS GroupMembers
    ,DENSE_RANK() OVER(ORDER BY 
        CASE WHEN CA_Data.XML_Value IS NULL 
        THEN CTE_Idents.Ident ELSE CA_Data.XML_Value END
    ) AS GroupID
FROM
    CTE_Idents
    CROSS APPLY
    (
        SELECT CTE_CleanResult.Ident+','
        FROM CTE_CleanResult
        WHERE CTE_CleanResult.AnchorIdent = CTE_Idents.Ident
        ORDER BY CTE_CleanResult.Ident FOR XML PATH(''), TYPE
    ) AS CA_XML(XML_Value)
    CROSS APPLY
    (
        SELECT CA_XML.XML_Value.value('.', 'NVARCHAR(MAX)')
    ) AS CA_Data(XML_Value)
WHERE
    CTE_Idents.Ident IS NOT NULL
ORDER BY Ident;

ফলাফল 1

+-------+--------------+---------+
| Ident | GroupMembers | GroupID |
+-------+--------------+---------+
| a     | a,b,c,       |       1 |
| b     | a,b,c,       |       1 |
| c     | a,b,c,       |       1 |
+-------+--------------+---------+

ফলাফল 2

+-------+--------------+---------+
| Ident | GroupMembers | GroupID |
+-------+--------------+---------+
| a     | a,c,g,h,l,   |       1 |
| b     | b,d,f,j,     |       2 |
| c     | a,c,g,h,l,   |       1 |
| d     | b,d,f,j,     |       2 |
| e     | e,k,         |       3 |
| f     | b,d,f,j,     |       2 |
| g     | a,c,g,h,l,   |       1 |
| h     | a,c,g,h,l,   |       1 |
| i     | i            |       4 |
| j     | b,d,f,j,     |       2 |
| k     | e,k,         |       3 |
| l     | a,c,g,h,l,   |       1 |
| z     | z            |       5 |
+-------+--------------+---------+

ফলাফল 3

+-------+--------------------------+---------+
| Ident |       GroupMembers       | GroupID |
+-------+--------------------------+---------+
| a     | a,b,c,d,e,f,g,h,j,k,l,m, |       1 |
| b     | a,b,c,d,e,f,g,h,j,k,l,m, |       1 |
| c     | a,b,c,d,e,f,g,h,j,k,l,m, |       1 |
| d     | a,b,c,d,e,f,g,h,j,k,l,m, |       1 |
| e     | a,b,c,d,e,f,g,h,j,k,l,m, |       1 |
| f     | a,b,c,d,e,f,g,h,j,k,l,m, |       1 |
| g     | a,b,c,d,e,f,g,h,j,k,l,m, |       1 |
| h     | a,b,c,d,e,f,g,h,j,k,l,m, |       1 |
| j     | a,b,c,d,e,f,g,h,j,k,l,m, |       1 |
| k     | a,b,c,d,e,f,g,h,j,k,l,m, |       1 |
| l     | a,b,c,d,e,f,g,h,j,k,l,m, |       1 |
| m     | a,b,c,d,e,f,g,h,j,k,l,m, |       1 |
+-------+--------------------------+---------+

কিভাবে এটা কাজ করে

আমি এই ব্যাখ্যার জন্য স্যাম্পল ডেটার দ্বিতীয় সেট ব্যবহার করব।

CTE_Idents

CTE_Idents Ident1 এবং Ident2 কলামে প্রদর্শিত সমস্ত আইডেন্টিফায়ারদের তালিকা দেয়। যেহেতু তারা যে কোনও ক্রমে হাজির হতে পারে আমরা উভয় কলামকে এক সাথে UNION করি। UNION যেকোন সদৃশও সরিয়ে দেয়।

+-------+
| Ident |
+-------+
| NULL  |
| a     |
| b     |
| c     |
| d     |
| e     |
| f     |
| g     |
| h     |
| i     |
| j     |
| k     |
| l     |
| z     |
+-------+

CTE_Pairs

CTE_Pairs উভয় দিকের গ্রাফের সমস্ত প্রান্তের তালিকা দেয়। আবার, UNION কোনও সদৃশ অপসারণ করতে ব্যবহৃত হয়।

+--------+--------+
| Ident1 | Ident2 |
+--------+--------+
| a      | c      |
| a      | g      |
| b      | f      |
| b      | j      |
| c      | a      |
| c      | h      |
| d      | f      |
| e      | k      |
| f      | b      |
| f      | d      |
| g      | a      |
| h      | c      |
| h      | l      |
| j      | b      |
| k      | e      |
| l      | h      |
+--------+--------+

CTE_Recursive

CTE_Recursive হল ক্যোয়ারির মূল অংশ যা প্রতিটি অনন্য সনাক্তকারী থেকে শুরু করে গ্রাফটিকে CTE_Recursive । এই শুরু সারিগুলি UNION ALL প্রথম অংশ দ্বারা উত্পাদিত হয়। UNION ALL Ident2 দ্বিতীয় অংশ পুনরাবৃত্তভাবে Ident2 সাথে Ident2 সাথে যুক্ত হয়ে নিজেকে যুক্ত করে। যেহেতু আমরা উভয় দিকের লিখিত সমস্ত প্রান্তের সাথে CTE_Pairs প্রাক-তৈরি CTE_Pairs , আমরা সর্বদা কেবল Ident2 কে Ident1 Ident2 সাথে লিঙ্ক করতে Ident1 এবং আমরা গ্রাফের সমস্ত পথ পাব। একই সময়ে ক্যোয়ারী IdentPath তৈরি করে - কমা-সীমাবদ্ধ IdentPath একটি স্ট্রিং যা এখন পর্যন্ত ট্র্যাভারস করা হয়েছে। এটি WHERE ফিল্টার ব্যবহৃত হয়:

CTE_Recursive.IdentPath NOT LIKE CAST('%,' + CTE_Pairs.Ident2 + ',%' AS varchar(8000))

এর আগে আমরা যে পরিচয়কারীটি পাথের মধ্যে আগে অন্তর্ভুক্ত করা হয়েছিল তা অতিক্রম করার সাথে সাথে সংযুক্ত নোডগুলির তালিকাটি শেষ হয়ে যাওয়ায় পুনরাবৃত্তি বন্ধ হয়ে যায়। AnchorIdent পুনরাবৃত্তির জন্য শুরুর AnchorIdent , এটি পরে গ্রুপের ফলাফলগুলিতে ব্যবহার করা হবে। Lvl সত্যিই ব্যবহৃত হয় না, আমি কী চলছে তা আরও ভাল করে বোঝার জন্য এটি অন্তর্ভুক্ত করেছি।

+-------------+--------+--------+-------------+-----+
| AnchorIdent | Ident1 | Ident2 |  IdentPath  | Lvl |
+-------------+--------+--------+-------------+-----+
| a           | a      | c      | ,a,c,       |   1 |
| a           | a      | g      | ,a,g,       |   1 |
| b           | b      | f      | ,b,f,       |   1 |
| b           | b      | j      | ,b,j,       |   1 |
| c           | c      | a      | ,c,a,       |   1 |
| c           | c      | h      | ,c,h,       |   1 |
| d           | d      | f      | ,d,f,       |   1 |
| e           | e      | k      | ,e,k,       |   1 |
| f           | f      | b      | ,f,b,       |   1 |
| f           | f      | d      | ,f,d,       |   1 |
| g           | g      | a      | ,g,a,       |   1 |
| h           | h      | c      | ,h,c,       |   1 |
| h           | h      | l      | ,h,l,       |   1 |
| j           | j      | b      | ,j,b,       |   1 |
| k           | k      | e      | ,k,e,       |   1 |
| l           | l      | h      | ,l,h,       |   1 |
| l           | h      | c      | ,l,h,c,     |   2 |
| l           | c      | a      | ,l,h,c,a,   |   3 |
| l           | a      | g      | ,l,h,c,a,g, |   4 |
| j           | b      | f      | ,j,b,f,     |   2 |
| j           | f      | d      | ,j,b,f,d,   |   3 |
| h           | c      | a      | ,h,c,a,     |   2 |
| h           | a      | g      | ,h,c,a,g,   |   3 |
| g           | a      | c      | ,g,a,c,     |   2 |
| g           | c      | h      | ,g,a,c,h,   |   3 |
| g           | h      | l      | ,g,a,c,h,l, |   4 |
| f           | b      | j      | ,f,b,j,     |   2 |
| d           | f      | b      | ,d,f,b,     |   2 |
| d           | b      | j      | ,d,f,b,j,   |   3 |
| c           | h      | l      | ,c,h,l,     |   2 |
| c           | a      | g      | ,c,a,g,     |   2 |
| b           | f      | d      | ,b,f,d,     |   2 |
| a           | c      | h      | ,a,c,h,     |   2 |
| a           | h      | l      | ,a,c,h,l,   |   3 |
+-------------+--------+--------+-------------+-----+

CTE_CleanResult

CTE_CleanResult কেবলমাত্র CTE_CleanResult থেকে প্রাসঙ্গিক অংশ ছেড়ে CTE_Recursive এবং আবার Ident2 ব্যবহার করে Ident1 এবং Ident2 2 উভয়কে একত্রিত করে।

+-------------+-------+
| AnchorIdent | Ident |
+-------------+-------+
| a           | a     |
| a           | c     |
| a           | g     |
| a           | h     |
| a           | l     |
| b           | b     |
| b           | d     |
| b           | f     |
| b           | j     |
| c           | a     |
| c           | c     |
| c           | g     |
| c           | h     |
| c           | l     |
| d           | b     |
| d           | d     |
| d           | f     |
| d           | j     |
| e           | e     |
| e           | k     |
| f           | b     |
| f           | d     |
| f           | f     |
| f           | j     |
| g           | a     |
| g           | c     |
| g           | g     |
| g           | h     |
| g           | l     |
| h           | a     |
| h           | c     |
| h           | g     |
| h           | h     |
| h           | l     |
| j           | b     |
| j           | d     |
| j           | f     |
| j           | j     |
| k           | e     |
| k           | k     |
| l           | a     |
| l           | c     |
| l           | g     |
| l           | h     |
| l           | l     |
+-------------+-------+

চূড়ান্ত নির্বাচন

এখন আমাদের প্রতিটি AnchorIdent জন্য কমা-বিচ্ছিন্ন AnchorIdent মানগুলির একটি স্ট্রিং তৈরি করতে হবে। FOR XML প্রযোজ্য CROSS APPLY এটি করে। DENSE_RANK() প্রতিটি AnchorIdent জন্য GroupID সংখ্যা গণনা করে।







sql-server-2008