1

階層を形成する一連のリンクを含むテーブルがあります。大きな問題は、各リンクが複数回 (異なる位置で) 使用される可能性があることです。各ノードの各「インスタンス」を区別できる必要があります。

たとえば、次のデータでは、リンク「DG」が数回表示されます。

╔════════════╦════════╗
║     SOURCE ║ TARGET ║
╠════════════╬════════╣
║     A      ║ B      ║
║     A      ║ C      ║
║     B      ║ D      ║
║     B      ║ E      ║
║     B      ║ F      ║
║     C      ║ D      ║
║     C      ║ E      ║
║     C      ║ F      ║
║     D      ║ G      ║
║     E      ║ D      ║
║     F      ║ D      ║
╚════════════╩════════╝

問題なく再帰 CTE を使用して階層を構築できますが、結果の各行に一意の ID を与え、それを親ノードの一意の ID にリンクしたいと考えています。

私の最初のアイデアは、この時点までを使用して各行に一意の ID を割り当て、Row_Number() + Max(ID)行に親 ID を継承させることでしたが、さらに読んで試行錯誤すると、これは機能しないことがわかりました :-(

この問題を解決する方法を知っている人はいますか (または、少なくとも手がかりを教えてください)。

結果は次のようになります。

╔═════════════╦═════════════╦═══════════╦═══════════╗
║ SOURCE_DESC ║ TARGET_DESC ║ Source_ID ║ Target_ID ║
╠═════════════╬═════════════╬═══════════╬═══════════╣
║     A       ║ B           ║         0 ║         1 ║
║     A       ║ C           ║         0 ║         2 ║
║     B       ║ D           ║         1 ║         6 ║
║     B       ║ E           ║         1 ║         7 ║
║     B       ║ F           ║         1 ║         8 ║
║     C       ║ D           ║         2 ║         3 ║
║     C       ║ E           ║         2 ║         4 ║
║     C       ║ F           ║         2 ║         5 ║
║     D       ║ G           ║         3 ║        13 ║
║     E       ║ D           ║         4 ║        11 ║
║     F       ║ D           ║         5 ║        10 ║
║     D       ║ G           ║         6 ║        14 ║
║     E       ║ D           ║         7 ║        12 ║
║     F       ║ D           ║         8 ║         9 ║
║     D       ║ G           ║         9 ║        18 ║
║     D       ║ G           ║        10 ║        17 ║
║     D       ║ G           ║        11 ║        16 ║
║     D       ║ G           ║        12 ║        15 ║
╚═════════════╩═════════════╩═══════════╩═══════════╝

ここで「DG」リンクが数回表示されますが、それぞれのインスタンスで異なる ID と異なる親 ID を持っています。

なんとかやり遂げましたが、自分のやり方には満足していません。あまり効率的ではないようです (この例では重要ではありませんが、より大きなセットでは非常に重要です!)

WITH JUNK_DATA 
     AS (SELECT *, 
                ROW_NUMBER() 
                  OVER ( 
                    ORDER BY SOURCE) RN 
         FROM   LINKS), 
     RECUR 
     AS (SELECT T1.SOURCE, 
                T1.TARGET, 
                CAST('ROOT' AS VARCHAR(MAX))      NAME, 
                1                                 AS RAMA, 
                CAST(T1.RN AS VARCHAR(MAX)) + ',' AS FULL_RAMA 
         FROM   JUNK_DATA T1 
                LEFT JOIN JUNK_DATA T2 
                       ON T1.SOURCE = T2.TARGET 
         WHERE  T2.TARGET IS NULL 
         UNION ALL 
         SELECT JUNK_DATA.SOURCE, 
                JUNK_DATA.TARGET, 
                CASE 
                  WHEN RAMA = 1 THEN (SELECT [DESC] 
                                      FROM   NAMES 
                                      WHERE  ID = JUNK_DATA.SOURCE) 
                  ELSE NAME 
                END      NAME, 
                RAMA + 1 AS RAMA, 
                FULL_RAMA 
                + CAST(JUNK_DATA.RN AS VARCHAR(MAX)) + ',' 
         FROM   (SELECT * 
                 FROM   JUNK_DATA)JUNK_DATA 
                INNER JOIN (SELECT * 
                            FROM   RECUR) RECUR 
                        ON JUNK_DATA.SOURCE = RECUR.TARGET), 
     FINAL_DATA 
     AS (SELECT T2.[DESC]          SOURCE_DESC, 
                T3.[DESC]          TARGET_DESC, 
                RECUR.*, 
                ROW_NUMBER() 
                  OVER ( 
                    ORDER BY RAMA) ID 
         FROM   RECUR 
                INNER JOIN NAMES T2 
                        ON RECUR.SOURCE = T2.ID 
                INNER JOIN NAMES T3 
                        ON RECUR.TARGET = T3.ID) 
SELECT T1.SOURCE_DESC, 
       T1.TARGET_DESC, 
       ISNULL(T2.ID, 0) AS SOURCE_ID, 
       T1.ID            TARGET_ID 
FROM   FINAL_DATA T1 
       LEFT JOIN (SELECT ID, 
                         FULL_RAMA 
                  FROM   FINAL_DATA)T2 
              ON LEFT(T1.FULL_RAMA, LEN(T1.FULL_RAMA) - CHARINDEX(',', 
                 REVERSE(T1.FULL_RAMA), 2)) 
                 + ',' = T2.FULL_RAMA 
ORDER  BY SOURCE_ID, 
          TARGET_ID 

SQL fiddleで確認してください。

4

0 に答える 0