Я согласен с фокусом @ 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 - остановить рекурсию по невидимым "поддеревьям" как можно скорее. I.e.:
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
Как обычно в случае проблем с производительностью, сравнительный анализ обеих версий на реалистичных данных необходим для уверенного принятия решения (это также помогает проверить, действительно ли они дают идентичные результаты ;-) - поскольку оптимизаторы механизмов БД иногда делают странные вещи для странных причины; -)
.