Sql 筛选出具有parentid的表中的子项

Sql 筛选出具有parentid的表中的子项,sql,sql-server,tsql,Sql,Sql Server,Tsql,我需要一些帮助来构造一个查询,让我过滤以下数据 Table: MyTree Id ParentId Visible ===================== 1 null 0 2 1 1 3 2 1 4 3 1 5 null 1 6 5 1 我希望查询结果如下: Id ParentId Visible ===================== 5 null 1

我需要一些帮助来构造一个查询,让我过滤以下数据

Table: MyTree
Id  ParentId  Visible
=====================
1   null      0
2   1         1
3   2         1
4   3         1
5   null      1
6   5         1
我希望查询结果如下:

Id  ParentId  Visible
=====================
5   null      1
6   5         1
也就是说,不应返回隐藏节点的所有子节点。更重要的是,层次结构的深度是不受限制的。现在不要回答“只需将2、3和4设置为可见=0”,因为非obviuos原因是不可能的。。。就像我在修复一个可怕的“遗留系统”

我在想这样的事情:

SELECT *
FROM MyTree m1
JOIN MyTree m2 ON m1.ParentId = m2.Id
WHERE m1.Visible = 1
AND (m1.ParentId IS NULL OR m2.Id IS NOT NULL)
对不起,有语法错误

但这只会过滤第一层,对吗?希望你能帮忙


编辑:完成标题,哎呀。该服务器是一款全新的MSSQL 2008服务器,但数据库以2000兼容模式运行

在SQL Server 2005+中:

WITH    q (id, parentid, visible) AS
        (
        SELECT  id, parentid, visible
        FROM    mytree
        WHERE   id = 5
        UNION ALL
        SELECT  m.id, m.parentid, m.visible
        FROM    q
        JOIN    mytree m
        ON      m.parentid = q.id
        WHERE   q.visible = 1
        )
SELECT  *
FROM    q

我不认为你需要的是一个单一的查询。这看起来更像是从代码中完成的,但仍然需要对DB进行多个查询

如果您真的需要从SQL执行此操作,我认为您最好使用游标并使用隐藏ID构建一个表。如果数据不经常更改,您可以将该“临时”表作为一种缓存


编辑:我站在正确的立场上(对于SQL 2005),今天也学到了一些新的东西:)

我同意@Quassnoi对递归CTE的关注(在SQL Server 2005或更高版本中),但我认为回答原始问题的逻辑不同:

WITH visall(id, parentid, visible) AS
   (SELECT  id, parentid, visible
    FROM    mytree
    WHERE   parentid IS NULL
        UNION ALL
    SELECT  m.id, m.parentid, m.visible & visall.visible AS visible
    FROM    visall
    JOIN    mytree m
      ON    m.parentid = visall.id
   )
SELECT  *
FROM    visall
WHERE   visall.visible = 1
表达相同逻辑的一种可能更优化的方法应该是在WHERE中尽可能多地进行可见检查——尽快停止沿不可见“子树”的递归。即:

WITH visall(id, parentid, visible) AS
   (SELECT  id, parentid, visible
    FROM    mytree
    WHERE   parentid IS NULL AND visible = 1
        UNION ALL
    SELECT  m.id, m.parentid, m.visible
    FROM    visall
    JOIN    mytree m
      ON    m.parentid = visall.id
    WHERE   m.visible = 1
   )
SELECT  *
FROM    visall

与通常的性能问题一样,在实际数据上对两个版本进行基准测试对于自信地做出决定是必要的(这也有助于检查它们是否确实产生相同的结果;-)--因为DB引擎的优化器有时会出于奇怪的原因做奇怪的事情;-)

我认为我接近提问者想要的,但不是完全。我认为这就是提问者想要的(SQL Server 2005+):


通用表表达式非常适合此类工作。

@JohannesH-我不确定我是否理解您要完成的任务。我认为答案很大程度上取决于您是使用SQL Server 2005+(可以执行递归CTE)还是使用SQL Server 2000-(无法执行)实际的SQL server是2008,但所讨论的数据库是在2000兼容模式下运行的。我完全同意应该在应用程序的代码中完成,但不幸的是,我没有源代码。维护遗产很糟糕。;)@亚历克斯:我想你离我要找的东西最近了。所以接受你的答案。你的解决方案将遍历不可见的节点,然后过滤掉它们。过滤掉CTE中不可见的节点,以节省一些递归步骤,不是更好吗?@JohannesH,很乐意提供帮助。是的,@Quassnoi,我确信我的代码可以改进(将可见检查移到何处,而不是在递归shd帮助的两个分支中选择——让我编辑以添加以这种方式优化的代码),但特殊的大小写“id=5”并不是询问者想要的,只是一个例子。
WITH    q (id) AS
        (
        SELECT  id
        FROM    mytree
        WHERE   parentid is null and visible=1
        UNION ALL
        SELECT  m.id
        FROM    q
        JOIN    mytree m
        ON      m.parentid = q.id
        WHERE   q.visible = 1
        )
SELECT  *
FROM    q