19 ноября 2008

Иерархические/рекурсивные запросы (Oracle/MS-SQL)

Медленно и нерешительно продвигается портирование одной нашей разработки из-под Oracle на MS-SQL. Очередной маленькой победой стал перевод иерархического запроса в рекурсивный, почти совместимый с SQL 1999 (не считая слова RECURSIVE).
Итак, в Oracle все просто – дерево в реляционной форме представляется как таблица, которая связывает каждый узел с его родителем.


parent child comment
-----------------------------
0 1 Root
1 2 Level1
1 5 Level1
1 7 Level1
2 3 Level2
2 4 Level2
4 6 Level3


На самом деле, узлы могут идти в произвольном порядке. Чтобы обойти дерево последовательно, слева направо, достаточно отправить запрос следующего вида:

  1. SELECT parent, child, comment, level
  2. FROM tree
  3. CONNECT BY parent = PRIOR child
  4. START WITH parent = 0


Здесь level – идентификатор, которому сопоставляется текущий уровень иерархии, CONNECT BY указывает, по каким полям производится связывание, причем PRIOR определяет направление движения, а START WITH указывает на начало иерархии.
Результатом данного запроса будет следующая таблица.

parent child comment
-----------------------------
0 1 Root
1 2 Level1
2 3 Level2
2 4 Level2
4 6 Level3
1 5 Level2
1 7 Level2


Чтобы оформить те же действия для MS-SQL, строится следующий запрос.

  1. WITH
  2. Rec (child, parent, comment, level)
  3. AS (
  4. SELECT child, parent, comment, 0 AS level FROM tree WHERE parent = 0
  5. UNION ALL
  6. SELECT tree.child, tree.parent, tree.comment , level + 1
  7. FROM tree, Rec
  8. WHERE tree.parent = Rec.child
  9. )
  10. SELECT child, parent, comment, level FROM Rec


Я это понимаю так. С помощью фразы WITH мы строим новую таблицу Rec, из которой последний SELECT извлекает данные. Таблица Rec состоит из двух частей: корня иерархии (SELECT child, parent, comment, 0 AS level FROM tree where parent = 0), объединенного (UNION ALL) с преобразованным при помощи рекурсивного обхода телом базовой таблицы (второй SELECT). level приходится эмулировать базовыми средствами SQL.

Комментариев нет: