2014-09-09 3 views
0

Итак, у меня есть древовидная структура, реализованная в виде вложенного набора. У меня также есть элемент управления TreeView, который отображает указанную древовидную структуру.Получение всех детей первого уровня из «открытых» элементов в модели вложенных множеств

Первоначально все элементы «закрыты», что означает, что я вижу только предметы первого уровня. Теперь пользователь может открывать/закрывать элементы по своему желанию, и я отслеживаю открытые элементы. Все открытые элементы вставляются в TreeView.

я получить их, используя эту инструкцию:

select opened.tt_id, tree_test.* 
    from tree_test as opened 
    join tree_test on tree_test.tt_nsleft between opened.tt_nsleft + 1 and opened.tt_nsright 
    and not exists (select * from tree_test t1 where t1.tt_nsleft between opened.tt_nsleft + 1 and opened.tt_nsright and tree_test.tt_nsleft between t1.tt_nsleft + 1 and t1.tt_nsright - 1) 
    where opened.tt_id in (1, 3, 8) 
order by tt_nsleft 

Пользователь может drilll вниз по желанию открытия 3-го или 4-го уровня элементов. И здесь я столкнулся с проблемой: если у пользователя есть некоторые элементы уровня 3-го, 4-го и т. Д., И теперь он закрывает своего родителя 1-го уровня, вышеупомянутый запрос все равно вернет все эти подпункты.

Каков наилучший способ «подтвердить» открытые элементы, чтобы остались только те, которые являются прямыми детьми на самом деле видимых элементов?

У меня есть решение, которое я думаю, что это довольно расточителен и он просто проверяет открытые элементы против результирующего набора:

select opened.tt_id, tree_test.* 
    from tree_test as opened 
    join tree_test on tree_test.tt_nsleft between opened.tt_nsleft + 1 and opened.tt_nsright 
    and not exists (select * from tree_test t1 where t1.tt_nsleft between opened.tt_nsleft + 1 and opened.tt_nsright and tree_test.tt_nsleft between t1.tt_nsleft + 1 and t1.tt_nsright - 1) 
    where opened.tt_id in (1, 3, 8) 
    and opened.tt_id in (
      select tree_test.tt_id 
      from tree_test as opened 
      join tree_test on tree_test.tt_nsleft between opened.tt_nsleft + case when opened.tt_nsleft = 1 then 0 else 1 end and opened.tt_nsright 
      and not exists (select * from tree_test t1 where t1.tt_nsleft between opened.tt_nsleft + 1 and opened.tt_nsright and tree_test.tt_nsleft between t1.tt_nsleft + 1 and t1.tt_nsright - 1) 
      where opened.tt_id in (1, 3, 8) 
    ) 
order by tt_nsleft 

Это работает, потому что результирующий набор содержит только первый уровень детей всех запрошенных пунктов , Если элемент не находится в результирующем наборе, он должен произойти из уровня ниже. Это, конечно, означает двойную работу для сервера, так как на самом деле нужно собрать данные дважды, чтобы иметь возможность отфильтровывать недопустимые открытые элементы.

Альтернативным решением будет поиск родительского элемента открытого объекта и проверка его против списка открытых элементов (+ null для элемента верхнего уровня). Однако запрос для этого не кажется намного легче.

Есть ли третий вариант, который я здесь отсутствует?

ответ

0

После долгих экспериментирования (и отсутствии) я нашел решение, которое столь же элегантно, как любой основной вложенной установленной операции:

select tree_test".* 
    from tree_test 
where not exists (
     select * 
      from tree_test parents 
     where tree_test.tt_nsleft between (parents.tt_nsleft + 1) and parents.tt_nsright 
     and not (parents.tt_id in (12384898975268895, 12384898975268897, 12384898975268902)) 
    ) 

Фактически, этот запрос говорит: «не выбрать все пункты, где нет родитель, чей идентификатор не входит в список «. Фактически, условие для указания «открытых» элементов произвольно, оно не обязательно должно быть идентификатором элемента.