在递归 CTE 中删除所有包含 ID 的较短子序列

Remove all shorter subsequences containing an ID in a recursive CTE

标题好乱!让我详细说明一下。

我有两张桌子;一个表示模块(可以包含其他模块的某种类型的网页)称为 module_info,另一个表示每个页面在模块层次结构中的位置称为 www_menu.

module_info:

+----------+------------+---------+
| moduleID | modulename | menu_id |
+----------+------------+---------+
|        1 | tests      |       1 |
|        2 | docs       |       2 |
|        3 | mail       |       3 |
|        4 | networks   |       4 |
|        5 | payroll    |       5 |
|        6 | admin      |       6 |
|        7 | travel     |       7 |
|        8 | bios       |       8 |
+----------+------------+---------+

www_menu:

+--------+--------+-------------------+------------------+
| menuID | parent |       title       |     location     |
+--------+--------+-------------------+------------------+
|      1 |      0 | Tests             | modules/tests    |
|      2 |      0 | Testing Documents | modules/docs     |
|      3 |      0 | Mailing Lists     | modules/mail     |
|      4 |      1 | Network Services  | modules/networks |
|      5 |      1 | Payroll           | modules/payroll  |
|      6 |      2 | Administration    | modules/admin    |
|      7 |      3 | Travel            | modules/travel   |
|      8 |      4 | Biographies       | modules/bios     |
+--------+--------+-------------------+------------------+

这是我显示层次结构的查询:

WITH [root] AS
(
    SELECT      www.menuID
                ,CAST(www.title + ' (' + mi.modulename + ')' AS VARCHAR(200)) AS [path]
                ,CAST(mi.id AS VARCHAR(20)) AS ids
    FROM        [dbo].[module_info] AS mi
    INNER JOIN  [dbo].[WWW_Menu] www ON www.menuid = mi.menu_id
    WHERE       www.location LIKE 'modules/%'
UNION ALL
    SELECT      leaf.menuID
                ,CAST([root].[path] + ' > ' + leaf.title AS VARCHAR(200))
                ,CAST([root].ids + ',' + CONVERT(VARCHAR(4), mi.id) AS VARCHAR(20))
    FROM        [dbo].[WWW_Menu] AS leaf
    INNER JOIN  [root] ON leaf.parent = [root].menuID
    INNER JOIN  [dbo].[module_info] AS mi ON leaf.menuID = mi.menu_id
)
SELECT [path], ids FROM [root]

以及上面两个表中 运行 的结果:

+----------------------------------------+-------+
|                  path                  |  ids  |
+----------------------------------------+-------+
| Tests                                  | 1     |
| Testing Documents                      | 2     |
| Mailing Lists                          | 3     |
| Network Services                       | 4     |
| Payroll                                | 5     |
| Administration                         | 6     |
| Travel                                 | 7     |
| Biographies                            | 8     |
| Tests > Network Services               | 1,4   |
| Tests > Payroll                        | 1,5   |
| Testing Documents > Administration     | 2,6   |
| Mailing Lists > Travel                 | 3,7   |
| Network Services > Biographies         | 4,8   |
| Tests > Network Services > Biographies | 1,4,8 |
+----------------------------------------+-------+

我将在 select 框中显示这些结果,以便在 selected 模块中搜索文档。事情是这样的; BiographiesNetwork Services > BiographiesTests > Network Services > Biographies 是同一个模块。

对于每个模块,我只想显示以该模块结尾的最长层次字符串

+----------------------------------------+-------+
|                  path                  |  ids  |
+----------------------------------------+-------+
| Tests                                  | 1     |
| Testing Documents                      | 2     |
| Mailing Lists                          | 3     |
| Tests > Network Services               | 1,4   |
| Tests > Payroll                        | 1,5   |
| Testing Documents > Administration     | 2,6   |
| Mailing Lists > Travel                 | 3,7   |
| Tests > Network Services > Biographies | 1,4,8 |
+----------------------------------------+-------+

也许这不是一个 SQL 问题,最好通过某种迭代 server-side 来完成。但是,针对此问题的 self-contained SQL 解决方案将是我的首选。谢谢。

一种方法会涉及大量的字符串操作。相反,当您浏览列表时,请跟踪添加到列表中的最后一个模块。然后使用 row_number() 获得每个 "last" 模块的最长:

WITH [root] AS (
    SELECT www.menuID,
           CAST(www.title + ' (' + mi.modulename + ')' AS VARCHAR(200)) AS [path],
           CAST(mi.id AS VARCHAR(20)) AS ids,
           mi.id as lastId
    FROM [dbo].[module_info] mi INNER JOIN
         [dbo].[WWW_Menu] www 
         ON www.menuid = mi.menu_id
    WHERE www.location LIKE 'modules/%'
    UNION ALL
    SELECT leaf.menuID,
           CAST([root].[path] + ' > ' + leaf.title AS VARCHAR(200)),
           CAST([root].ids + ',' + CONVERT(VARCHAR(4), mi.id) AS VARCHAR(20)),
           mi.id
    FROM [dbo].[WWW_Menu] leaf INNER JOIN
         [root]
         ON leaf.parent = [root].menuID INNER JOIN
         [dbo].[module_info] mi
         ON leaf.menuID = mi.menu_id
    )
SELECT [path], ids
FROM (SELECT r.*,
             ROW_NUMBER() OVER (PARTITION BY lastId ORDER BY length(path) DESC) as seqnum
      FROM [root] r
     ) r
WHERE seqnum = 1;