[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Построение древовидного списка одним запросом
Страницы: 1, 2
sergeiss
На форуме периодически всплывает один и тот же вопрос: как выбрать древовидную структуру из БД одним запросом?
Когда речь идет про MySQL, то какие только варианты не предлагаются... Начиная от функций в БД и заканчивая простой выборкой и разбором данных уже в PHP.
Но если речь идет про Постгре, то тут эта задача реально решается одним запросом! Потому что в Постгре изначально заложены такие возможности.
А в этой теме я хочу продемонстрировать, как это делается. То есть, поумничать решил wink.gif

Итак, есть таблица
CREATE TABLE id_tree
(
id serial NOT NULL,
id_parent integer DEFAULT 0,
id_subcut integer DEFAULT 0,
id_name character varying(10)
)


id - это целое автоинкрементное поле.
id_parent - целое число, показывает айди родителя; если == 0, то это корневой элемент
id_subcut - показывает номер подкатегории; это именно номер, а не айди.
id_name - имя категории; в данном случае специально введено в виде, показывающем место элемента в структуре, чтобы легче было контролировать результат.

Вводим тестовые данные в таблицу:

id id_parent id_subcut id_name
1 0 0 val 1
2 0 0 val 2
3 0 0 val 3
4 0 0 val 4
5 1 1 val 1_1
6 1 2 val 1_2
7 1 3 val 1_3
8 2 1 val 2_1
9 2 2 val 2_2
10 4 1 val 4_1
11 4 2 val 4_2
12 1 5 val 1_5
13 1 4 val 1_4
14 9 1 val 2_2_1


Запрос, который делает выборку, достаточно "витиеватый" на первый взгляд, но на самом деле достаточно простой.

Его упрощенный вид такой:
WITH RECURSIVE id_tree_selection (..параметры...) as ...сам...запрос...

select ...список.полей... from id_tree_selection
order by path

где path - это внутренний параметр, используемый для целей сортировки результата.

Суть такая: сначала готовится "предварительная" выборка (которая оказывается в итоге основной), посредством ключевого слова WITH, входящего в команду SELECT. Используя слово RECURSIVE, позволяем (предварительному) запросу обращаться к самому себе, с другими параметрами. Что и позволяет, собственно говоря, собрать всю древовидную структуру.

И запрос целиком:
WITH RECURSIVE id_tree_selection (id, id_parent, id_subcut, id_name, path, tree_index, cycle, depth) as
(
SELECT id, id_parent, id_subcut, id_name, array[row( id, id_parent, id_subcut)],
cast( id as varchar(100)) as tree_index, false, 1
from id_tree where id_parent=0
UNION all
SELECT
main.id, main.id_parent, main.id_subcut, main.id_name, path || row( main.id_parent, main.id_subcut),
cast( cut.tree_index || '.' || main.id_subcut as varchar(100) ) as tree_index,
row ( main.id, main.id_parent, main.id_subcut ) = any( path ), cut.depth +1
FROM id_tree main, id_tree_selection cut
WHERE
cut.id = main.id_parent and not cycle
)

select id, id_parent, id_subcut, tree_index, depth, id_name
from id_tree_selection
order by path


В предварительном запросе идет выборка некоторой строки, к которой прицепляются все ее потомки (!). Внутри запроса формируются дополнительные параметры, т.е. отсутствующие в основной таблице:
path - для итоговой сортировки (в итоговый результат не выводится)
cycle - для контроля данных, чтобы исключить дублирование строк (в итоговый результат не выводится)
tree_index - показывает вложенность категории в виде айди-точка-подкатегория-точка-подкатегория-точка-подкатегория... - и так до тех пор, пока не закончатся все подкатегории
depth - глубина вложенности категории

Результат выборки:
id	 id_parent	 id_subcut	 tree_index	 depth	 id_name
1 0 0 1 1 val 1
5 1 1 1.1 2 val 1_1
6 1 2 1.2 2 val 1_2
7 1 3 1.3 2 val 1_3
13 1 4 1.4 2 val 1_4
12 1 5 1.5 2 val 1_5
2 0 0 2 1 val 2
8 2 1 2.1 2 val 2_1
9 2 2 2.2 2 val 2_2
14 9 1 2.2.1 3 val 2_2_1
3 0 0 3 1 val 3
4 0 0 4 1 val 4
10 4 1 4.1 2 val 4_1
11 4 2 4.2 2 val 4_2


То есть, результат получен корректный, запрос работает исправно wink.gif

Данный запрос, естественно, похож на запрос из хэлпа smile.gif. Но на самом деле я его проработал, понял смысл каждой буковки. И только после этого выложил в этой теме. А он и не может быть на него не похож, потому что именно эта структура была создана для такого рода выборок, т.е. для "древовидных" данных.

Сразу хочу предупредить - эта тема вовсе не для "холиварных" войн была создана. Любые проявления таких "войн" будут безжалостно удаляться из темы.

PS. И закреплю эту тему... Если чего - открепить недолго.

_____________
* Хэлп по PHP
* Описалово по JavaScript
* Хэлп и СУБД для PostgreSQL

* Обучаю PHP, JS, вёрстке. Интерактивно и качественно. За разумные деньги.

* "накапливаю умение телепатии" (С) и "гуглю за ваш счет" (С)

user posted image
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.