LEVEL в Oracle SQL. Введение
LEVEL — псевдостолбец иерархических запросов в Oracle SQL. Он показывает глубину текущей строки в дереве (корень = 1) при использовании CONNECT BY и START WITH.
Кроме обхода иерархий, трюк CONNECT BY с LEVEL применяют как «генератор строк» — для построения числовых и календарных серий, развертки интервалов и т. п.
Где используют
- Иерархии: корни, листья, ограничение глубины, визуализация дерева, агрегаты по поддеревьям.
- Серии значений: числа 1..N, диапазоны дат/часов, искусственные ключи для тестовых наборов.
- Развертка интервалов: детализировать «с начала по конец» по дням/месяцам.
- Эксплоды: дублировать строки по количеству (explode), строить временные сетки.
Синтаксис
-- Иерархия
SELECT ..., LEVEL
FROM t
START WITH root_condition
CONNECT BY PRIOR parent_id = child_id;
-- Генератор строк 1..N
SELECT LEVEL AS n
FROM dual
CONNECT BY LEVEL <= :N;LEVELдоступен в спискеSELECT, предикатах и группировке.- Сортировку внутри ветвей выполняйте через
ORDER SIBLINGS BY. - Для ограничения глубины фильтруйте
WHERE LEVEL <= k(помните, что это фильтр после построения дерева).
100 примеров
1. Вывести оргструктуру с отступами
SELECT
LPAD(' ', (LEVEL-1)*2) || emp_name AS emp_name,
emp_id, manager_id, LEVEL
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id
ORDER SIBLINGS BY emp_name;2. Фильтр максимум до трёх уровней
SELECT emp_id, emp_name, LEVEL
FROM employees
WHERE LEVEL <= 3
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;3. Только листья дерева (признак CONNECT_BY_ISLEAF)
SELECT emp_id, emp_name
FROM employees
WHERE CONNECT_BY_ISLEAF = 1
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;4. Корневое значение для узла (CONNECT_BY_ROOT)
SELECT emp_id, CONNECT_BY_ROOT emp_id AS root_id, LEVEL
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;5. Путь «корень → узел» с SYS_CONNECT_BY_PATH
SELECT emp_id,
SYS_CONNECT_BY_PATH(emp_name, ' / ') AS path
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;6. Стабильная сортировка соседей
SELECT emp_id, emp_name, LEVEL
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id
ORDER SIBLINGS BY emp_name;7. Подсчёт узлов на каждом уровне
SELECT LEVEL, COUNT(*) AS cnt
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id
GROUP BY LEVEL
ORDER BY LEVEL;8. Только заданный уровень глубины
SELECT emp_id, emp_name, LEVEL
FROM employees
WHERE LEVEL = 2
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;9. Количество листьев по корням
SELECT CONNECT_BY_ROOT emp_id AS root_id,
SUM(CASE WHEN CONNECT_BY_ISLEAF = 1 THEN 1 ELSE 0 END) AS leaf_cnt
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id
GROUP BY CONNECT_BY_ROOT emp_id;10. Числовой ряд 1..10 с генератором строк
SELECT LEVEL AS n
FROM dual
CONNECT BY LEVEL <= 10;11. Календарь: дни месяца 1..31
SELECT (DATE '2025-01-01' + LEVEL - 1) AS dt
FROM dual
CONNECT BY LEVEL <= 31;12. Генерация почасового ряда за сутки
SELECT (TRUNC(SYSTIMESTAMP, 'DD') + NUMTODSINTERVAL(LEVEL-1,'HOUR')) AS ts
FROM dual
CONNECT BY LEVEL <= 24;13. Серия чисел и JOIN c таблицей
SELECT t.id, s.n
FROM t
CROSS JOIN (
SELECT LEVEL AS n FROM dual CONNECT BY LEVEL <= 5
) s;14. Развёртка интервалов по дням
SELECT id, (start_date + LEVEL - 1) AS day_dt
FROM periods
CONNECT BY PRIOR id = id
AND PRIOR SYS_GUID() IS NOT NULL
AND LEVEL <= (end_date - start_date + 1);15. Дублирование строк по количеству (explode)
SELECT o.order_id, o.product_id
FROM orders o
CONNECT BY PRIOR order_id = order_id
AND PRIOR SYS_GUID() IS NOT NULL
AND LEVEL <= o.qty;16. Иерархия категорий с подсветкой глубины
SELECT
LPAD('-', LEVEL*2, '-') || cat_name AS titled_name
FROM categories
START WITH parent_id IS NULL
CONNECT BY PRIOR cat_id = parent_id;17. Предки узла: обратный подъём
SELECT emp_id, manager_id, LEVEL
FROM employees
START WITH emp_id = :leaf_id
CONNECT BY emp_id = PRIOR manager_id;18. Подсчёт потомков через подзапрос
SELECT e.emp_id,
(SELECT COUNT(*)
FROM employees
START WITH emp_id = e.emp_id
CONNECT BY PRIOR emp_id = manager_id
) AS descendants
FROM employees e;19. Первые N уровней для визуализации
SELECT emp_id, emp_name, LEVEL
FROM employees
WHERE LEVEL <= :max_depth
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;20. Разделитель блоков иерархии по отделам
SELECT dept_id, emp_id, emp_name, LEVEL
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id
ORDER SIBLINGS BY dept_id, emp_name;Еще 20 примеров.
21. Серия дат между границами (на каждую запись)
SELECT id, (date_from + LEVEL - 1) AS d
FROM ranges
CONNECT BY PRIOR id = id
AND PRIOR SYS_GUID() IS NOT NULL
AND LEVEL <= (date_to - date_from + 1);22. Серия месяцев (первое число каждого)
SELECT ADD_MONTHS(TRUNC(SYSDATE, 'YYYY'), LEVEL-1) AS month_start
FROM dual
CONNECT BY LEVEL <= 12;23. Счёт узлов на глубине ≥ 3
SELECT COUNT(*) AS deep_nodes
FROM employees
WHERE LEVEL >= 3
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;24. Формирование ключа «root:node:depth»
SELECT CONNECT_BY_ROOT emp_id || ':' || emp_id || ':' || LEVEL AS key
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;25. Сортировка ветвей, затем глобальная сортировка
SELECT *
FROM (
SELECT emp_id, emp_name, LEVEL
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id
ORDER SIBLINGS BY emp_name
)
ORDER BY LEVEL, emp_name;26. Маркировка циклов с NOCYCLE
SELECT emp_id, manager_id, CONNECT_BY_ISCYCLE AS cyc, LEVEL
FROM employees
START WITH manager_id IS NULL
CONNECT BY NOCYCLE PRIOR emp_id = manager_id;27. Фильтр ветвей по статусу родителя
SELECT emp_id, emp_name, LEVEL
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id
AND PRIOR status = 'ACTIVE';28. Только корни (глубина = 1)
SELECT emp_id, emp_name, LEVEL
FROM employees
WHERE LEVEL = 1
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;29. Только третий уровень
SELECT emp_id, emp_name
FROM employees
WHERE LEVEL = 3
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;30. Идентификатор корня по узлу
SELECT emp_id, CONNECT_BY_ROOT emp_id AS root_id, LEVEL
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;31. Нумерация узлов в пределах корня
SELECT
CONNECT_BY_ROOT emp_id AS root_id,
ROW_NUMBER() OVER (PARTITION BY CONNECT_BY_ROOT emp_id ORDER BY LEVEL) AS rn,
emp_id, LEVEL
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;32. Листья с путями
SELECT SYS_CONNECT_BY_PATH(emp_name, ' / ') AS path
FROM employees
WHERE CONNECT_BY_ISLEAF = 1
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;33. Только подчинённые (без корней)
SELECT emp_id, emp_name
FROM employees
WHERE LEVEL > 1
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;34. Формирование календарной сетки неделю вперёд
SELECT TRUNC(SYSDATE) + LEVEL - 1 AS dt
FROM dual
CONNECT BY LEVEL <= 7;35. Серия часов внутри даты
SELECT TRUNC(SYSDATE) + (LEVEL-1)/24 AS hour_ts
FROM dual
CONNECT BY LEVEL <= 24;36. Серия минут на 15‑минутной сетке
SELECT TRUNC(SYSDATE) + NUMTODSINTERVAL((LEVEL-1)*15,'MINUTE') AS ts
FROM dual
CONNECT BY LEVEL <= 96;37. Расширение периодов по минутам (пример)
SELECT id, start_ts + NUMTODSINTERVAL(LEVEL-1,'MINUTE') AS ts
FROM intervals
CONNECT BY PRIOR id = id
AND PRIOR SYS_GUID() IS NOT NULL
AND LEVEL <= (end_ts - start_ts) * 24*60 + 1;38. Развертка количества в заказах по единицам
SELECT order_id, product_id, LEVEL AS unit_no
FROM order_items
CONNECT BY PRIOR order_item_id = order_item_id
AND PRIOR SYS_GUID() IS NOT NULL
AND LEVEL <= quantity;39. Дата‑серия между двумя параметрами
SELECT (DATE :d1 + LEVEL - 1) AS d
FROM dual
CONNECT BY LEVEL <= (:d2 - :d1 + 1);40. Серия для тестовых идентификаторов
SELECT 'USR' || TO_CHAR(LEVEL, 'FM0000') AS user_code
FROM dual
CONNECT BY LEVEL <= 100;Еще 20 примеров.
41. Свод по уровням глубины
SELECT LEVEL, SUM(salary) AS total_sal
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id
GROUP BY LEVEL
ORDER BY LEVEL;42. Отсечь ветви глубже заданной
SELECT emp_id, emp_name
FROM employees
WHERE LEVEL <= :k
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;43. Вложенная иерархия: отделы → сотрудники
SELECT d.dept_name, e.emp_name, LEVEL
FROM departments d
JOIN employees e ON e.dept_id = d.dept_id
START WITH d.parent_id IS NULL
CONNECT BY PRIOR d.dept_id = d.parent_id;44. Иерархия категорий с фильтром листьев
SELECT cat_id, cat_name, LEVEL
FROM categories
START WITH parent_id IS NULL
CONNECT BY PRIOR cat_id = parent_id
HAVING COUNT(*) >= 0;45. Пути идентификаторов через SYS_CONNECT_BY_PATH
SELECT cat_id, SYS_CONNECT_BY_PATH(cat_id, '/') AS id_path
FROM categories
START WITH parent_id IS NULL
CONNECT BY PRIOR cat_id = parent_id;46. Сортировка siblings по имени, затем по id
SELECT emp_id, emp_name, LEVEL
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id
ORDER SIBLINGS BY emp_name, emp_id;47. Серия рабочих дней (без выходных)
SELECT d
FROM (
SELECT TRUNC(SYSDATE, 'IW') + LEVEL - 1 AS d
FROM dual
CONNECT BY LEVEL <= 7
)
WHERE TO_CHAR(d,'DY','NLS_DATE_LANGUAGE=ENGLISH') NOT IN ('SAT','SUN');48. Серия месяцев назад на год
SELECT ADD_MONTHS(TRUNC(SYSDATE,'MM'), -(LEVEL-1)) AS month_start
FROM dual
CONNECT BY LEVEL <= 12;49. Суммарная глубина дерева
SELECT SUM(LEVEL) AS depth_sum
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;50. Максимальная глубина через агрегат
SELECT MAX(LEVEL) AS max_depth
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;51. Подсветка глубины в отчёте
SELECT RPAD('*', LEVEL, '*') AS stars, emp_name
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;52. Иерархия файловой системы
SELECT path, LEVEL
FROM files
START WITH parent_id IS NULL
CONNECT BY PRIOR file_id = parent_id;53. Выборка ровно на втором уровне по условию
SELECT emp_id, emp_name
FROM employees
WHERE LEVEL = 2 AND dept_id = :d
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;54. Серия для разметки часов работы
SELECT TO_CHAR(TRUNC(SYSDATE) + (LEVEL-1)/24,'HH24:MI') AS hh
FROM dual
CONNECT BY LEVEL <= 24;55. Построение шкалы процентов 0..100
SELECT LEVEL-1 AS pct
FROM dual
CONNECT BY LEVEL <= 101;56. Счетчик для распределения по корзинам
SELECT CASE WHEN MOD(LEVEL,2)=0 THEN 'even' ELSE 'odd' END AS bucket, COUNT(*) cnt
FROM dual
CONNECT BY LEVEL <= 100
GROUP BY CASE WHEN MOD(LEVEL,2)=0 THEN 'even' ELSE 'odd' END;57. Серия дат с шагом 7 дней
SELECT (DATE '2025-01-01' + 7*(LEVEL-1)) AS week_start
FROM dual
CONNECT BY LEVEL <= 8;58. Валидатор глубины: только до 5 уровней
SELECT COUNT(*) AS ok_rows
FROM employees
WHERE LEVEL <= 5
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;59. Подсчёт узлов ниже целевого корня
SELECT COUNT(*) AS cnt
FROM employees
START WITH emp_id = :root
CONNECT BY PRIOR emp_id = manager_id;60. Серия для теста производительности
EXPLAIN PLAN FOR
SELECT /* level test */ * FROM dual CONNECT BY LEVEL <= 10000;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());Еще 20 примеров.
61. Глубина для каждого узла и корень
SELECT emp_id, CONNECT_BY_ROOT emp_id AS root_id, LEVEL AS depth
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;62. Серия для генерации случайных данных
SELECT DBMS_RANDOM.STRING('U', 8) AS s
FROM dual
CONNECT BY LEVEL <= 100;63. Серия для пересечения с календарём
SELECT cal.d
FROM calendar cal
JOIN (SELECT TRUNC(SYSDATE) + LEVEL - 1 AS d FROM dual CONNECT BY LEVEL <= 30) s
ON cal.d = s.d;64. Серия по количеству дней в месяце
SELECT TRUNC(ADD_MONTHS(SYSDATE,1),'MM') - TRUNC(SYSDATE,'MM') AS days_in_month FROM dual;
SELECT TRUNC(SYSDATE,'MM') + LEVEL - 1 AS d
FROM dual
CONNECT BY LEVEL <= (TRUNC(ADD_MONTHS(SYSDATE,1),'MM') - TRUNC(SYSDATE,'MM'));65. Серия id для выборки первых N строк
SELECT * FROM (
SELECT t.*, LEVEL AS rn
FROM t
CONNECT BY LEVEL <= :n
) WHERE rn <= :n;66. Серия номеров страниц (пагинация)
SELECT LEVEL AS page_no
FROM dual
CONNECT BY LEVEL <= CEIL(:total / :per_page);67. Глубина и число потомков в одной выборке
SELECT e.emp_id, LEVEL AS depth,
(SELECT COUNT(*) FROM employees START WITH emp_id = e.emp_id CONNECT BY PRIOR emp_id = manager_id) AS subcnt
FROM employees e
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;68. Расширение интервалов по месяцам
SELECT id, ADD_MONTHS(start_month, LEVEL-1) AS m
FROM months_range
CONNECT BY PRIOR id = id
AND PRIOR SYS_GUID() IS NOT NULL
AND LEVEL <= MONTHS_BETWEEN(end_month, start_month) + 1;69. Серия для теста индекса по дате
SELECT TRUNC(SYSDATE) + LEVEL - 1 AS d
FROM dual
CONNECT BY LEVEL <= 365;70. Глубина и путь для BOM
SELECT parent_part, child_part, LEVEL,
SYS_CONNECT_BY_PATH(child_part,'/') AS path
FROM bom
START WITH parent_part = :p
CONNECT BY PRIOR child_part = parent_part;71. Ограничение глубины и фильтр листьев
SELECT child_part, LEVEL
FROM bom
WHERE LEVEL <= 3 AND CONNECT_BY_ISLEAF = 1
START WITH parent_part = :p
CONNECT BY PRIOR child_part = parent_part;72. Серия кварталов текущего года
SELECT ADD_MONTHS(TRUNC(SYSDATE,'YYYY'), (LEVEL-1)*3) AS q_start
FROM dual
CONNECT BY LEVEL <= 4;73. Серия номеров недель ISO
SELECT TO_NUMBER(TO_CHAR(TRUNC(SYSDATE,'IYYY') + (LEVEL-1)*7,'IW')) AS iw
FROM dual
CONNECT BY LEVEL <= 53;74. Серия для sample‑данных пользователей
SELECT 'user_' || LEVEL AS uname
FROM dual
CONNECT BY LEVEL <= 20;75. Глубина узлов отделов и сортировка
SELECT dept_id, dept_name, LEVEL
FROM departments
START WITH parent_id IS NULL
CONNECT BY PRIOR dept_id = parent_id
ORDER SIBLINGS BY dept_name;76. Серия с шагом 0.5 дня
SELECT TRUNC(SYSDATE) + (LEVEL-1)*0.5 AS halfday
FROM dual
CONNECT BY LEVEL <= 10;77. Серия с шагом в минутах (5м)
SELECT TRUNC(SYSDATE) + NUMTODSINTERVAL((LEVEL-1)*5,'MINUTE') AS ts
FROM dual
CONNECT BY LEVEL <= 12;78. Серия дней для каждой записи логов (пример)
SELECT l.id, TRUNC(l.ts) + LEVEL - 1 AS d
FROM logs l
CONNECT BY PRIOR id = id
AND PRIOR SYS_GUID() IS NOT NULL
AND LEVEL <= :days;79. Глубина в треде комментариев и порядок «соседей»
SELECT LPAD(' ', (LEVEL-1)*2) || author || ': ' || text AS thread_line, created_at
FROM comments
START WITH parent_id IS NULL
CONNECT BY PRIOR comment_id = parent_id
ORDER SIBLINGS BY created_at;80. Серия идентификаторов для тестовых вставок
SELECT LEVEL AS id FROM dual CONNECT BY LEVEL <= 100;Еще 20 примеров.
81. Серия часов для сводной таблицы
SELECT TO_CHAR(TRUNC(SYSDATE) + (LEVEL-1)/24,'HH24') AS hh
FROM dual
CONNECT BY LEVEL <= 24;82. Глубина и количество узлов на глубине
SELECT LEVEL, COUNT(*) cnt
FROM categories
START WITH parent_id IS NULL
CONNECT BY PRIOR cat_id = parent_id
GROUP BY LEVEL
ORDER BY LEVEL;83. Пути и глубина для категорий
SELECT cat_id, SYS_CONNECT_BY_PATH(cat_name,' > ') AS p, LEVEL
FROM categories
START WITH parent_id IS NULL
CONNECT BY PRIOR cat_id = parent_id;84. Серия дат от первого дня месяца
SELECT TRUNC(SYSDATE,'MM') + LEVEL - 1 AS d
FROM dual
CONNECT BY LEVEL <= TO_NUMBER(TO_CHAR(LAST_DAY(SYSDATE),'DD'));85. Серия номеров кварталов 1..4
SELECT LEVEL AS q FROM dual CONNECT BY LEVEL <= 4;86. Серия шагов 10..100 с шагом 10
SELECT LEVEL*10 AS step FROM dual CONNECT BY LEVEL <= 10;87. Серия для «топ‑N» имитации
SELECT * FROM (
SELECT t.*, LEVEL AS rn
FROM t
CONNECT BY LEVEL <= :n
) WHERE rn <= :n;88. Свод глубины по корню
SELECT CONNECT_BY_ROOT emp_id AS root_id, MAX(LEVEL) AS depth
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id
GROUP BY CONNECT_BY_ROOT emp_id;89. Серия дат «вчера..сегодня..завтра»
SELECT TRUNC(SYSDATE) + LEVEL - 2 AS d
FROM dual
CONNECT BY LEVEL <= 3;90. Серия букв через CHR
SELECT CHR(64 + LEVEL) AS letter
FROM dual
CONNECT BY LEVEL <= 26;91. Серия для генерации пар (i,j)
SELECT a.n AS i, b.n AS j
FROM (SELECT LEVEL AS n FROM dual CONNECT BY LEVEL <= 3) a
CROSS JOIN (SELECT LEVEL AS n FROM dual CONNECT BY LEVEL <= 3) b;92. Серия для распределения по сменам 1..3
SELECT MOD(LEVEL-1,3)+1 AS shift_no, COUNT(*) cnt
FROM dual
CONNECT BY LEVEL <= 30
GROUP BY MOD(LEVEL-1,3)+1;93. Серия по дням вокруг даты на 15 дней
SELECT TRUNC(:d) + LEVEL - 16 AS d
FROM dual
CONNECT BY LEVEL <= 31;94. Вывести оргструктуру с отступами
SELECT
LPAD(' ', (LEVEL-1)*2) || emp_name AS emp_name,
emp_id, manager_id, LEVEL
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id
ORDER SIBLINGS BY emp_name;95. Фильтр максимум до трёх уровней
SELECT emp_id, emp_name, LEVEL
FROM employees
WHERE LEVEL <= 3
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;96. Только листья дерева (признак CONNECT_BY_ISLEAF)
SELECT emp_id, emp_name
FROM employees
WHERE CONNECT_BY_ISLEAF = 1
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;97. Корневое значение для узла (CONNECT_BY_ROOT)
SELECT emp_id, CONNECT_BY_ROOT emp_id AS root_id, LEVEL
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;98. Путь «корень → узел» с SYS_CONNECT_BY_PATH
SELECT emp_id,
SYS_CONNECT_BY_PATH(emp_name, ' / ') AS path
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id;99. Стабильная сортировка соседей
SELECT emp_id, emp_name, LEVEL
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id
ORDER SIBLINGS BY emp_name;100. Подсчёт узлов на каждом уровне
SELECT LEVEL, COUNT(*) AS cnt
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR emp_id = manager_id
GROUP BY LEVEL
ORDER BY LEVEL;Частые ошибки и подводные камни
- Где писать ограничение глубины. Условие по
LEVELвWHEREрежет результат после построения. Иногда лучше ограничивать ветви дополнительными условиями вCONNECT BY. - Неправильное направление связи. Проверьте, где стоит
PRIOR, иначе уровни «перевернутся». - Порядок внутри ветвей. Используйте
ORDER SIBLINGS BY; обычныйORDER BYперемешает ветви. - Производительность. Глубокие/широкие деревья растут экспоненциально — добавляйте селективные предикаты и лимиты глубины.
Альтернативы
- Рекурсивный
WITHнаUNION ALL— гибче для нетривиальных правил обхода. - Серии без иерархий — через календарные таблицы или генераторы (внешние источники данных).
Заключение
LEVEL удобен как для иерархий, так и для генерации рядов. Контролируйте глубину, сортировку «соседей» и используйте проверенные шаблоны для серий чисел и дат.
Документация
Oracle SQL Language Reference — Hierarchical Queries
LEVEL, CONNECT_BY_ROOT, CONNECT_BY_ISLEAF, SYS_CONNECT_BY_PATH
Следующая статья:
PRIOR в Oracle SQL: как правильно связать родителя и потомка в иерархических запросах