过滤掉parentid表中的孩子

时间:2009-06-19 13:48:30

标签: 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
6   5         1

也就是说,不应该返回隐藏节点的所有子节点。更重要的是,层次结构的深度不受限制。现在不要回答“只是将2,3和4设置为可见= 0”,因为非显而易见的原因是不可能的......就像我正在修复一个可怕的“遗留系统”。

我想的是:

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兼容模式下运行。

4 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

我同意@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

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

答案 2 :(得分:1)

我认为Quassnoi接近提问者想要的,但并不完全。我认为这就是提问者正在寻找的东西(SQL Server 2005 +):

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

通用表格表达式非常适合这种工作。

答案 3 :(得分:-1)

我认为单个查询无法满足您的需求。这看起来更像是从代码中做的事情,但仍然需要对DB进行多次查询。

如果你真的需要从SQL做到这一点,我认为你最好的选择是使用游标并构建一个隐藏ID的表。如果数据不经常更改,您可以将该“临时”表保留为一种缓存。

编辑:我已经纠正(对于SQL 2005)并且今天也学到了新的知识:)