我需要一些帮助来构建一个允许我过滤以下数据的查询。
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兼容模式下运行。
答案 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)并且今天也学到了新的知识:)