пятница, 31 января 2020 г.

IT заметки. Иерархия.

Иерархия встречается часто в реляционных базах данных. Структуры административного деления стран, организационная структура или документооборот по договору - древовидные графы.
Распространенной практикой является хранение сущностей одного уровня иерархии в отдельных таблицах. К примеру, административное деление страны будет иметь три таблицы Страна, Регион и Город. Эта методика хорошо работает пока к сущностям (город, регион, страна) не потребуется привязать какие-то однородные данные (статистику по населению, ВВП и т.п.) или дерево получает еще один сущностный уровень.
Конечно, статистику можно хранить в  XML или JSON самих записей, но это сильно увеличит размер записи и потребует дополнительного индексирования, усложнит ее обработку. Правильнее было сложить статистику в отдельную таблицу, чтобы не увеличивать размер записи и увеличивать время обращения к статистике. Если каждый сущностный уровень хранится в отдельной таблице, то нужно делать отдельные статистические таблицы (для страны, региона, города) или пользоваться полиморфными связями (это когда поле вторичного ключа связано с полем первичного ключа нескольких таблиц). Оба варианта затратны и сложны в масштабировании.
Решить эту задачу поможет хранение иерархии в одной таблице. Известно много вариантов хранения древовидных структур данных в одной таблице. MS SQL Server предлагает типовое решение. Оно появилось ещё в MS SQL Server 2008 R2, но его описание почти не встречается в литературе по MS SQL. Удобен он тем, что для поиска пути от корня к текущему элементу не надо проходить промежуточные узлы по таблице. Речь идет о HIERARCHYID.
HIERARCHYID - системный тип данных для положения представления в иерархии. В нем кодируется путь от корня дерева к узлу. Хранится путь в двоичном виде. Для удобства есть функция ToString(), которая возвращает текстовое представление:
'/' - корень дерева,
'/1/', '/2/' - 1-ый и 2-ой потомки корня
'/1/2/', '/2/1/', '/2/2/' - 2-ой потомок 1-ого потомка корня, 1-ый и 2-ой потомок 2-ого потомка корня.
При операции INSERT можно пользоваться текстовым написанием HIERARCHID, что может сильно упростить множественную вставку, так как IDENTITY не поддерживается и приходится генерировать ключи уровня руками.
Основные функции:
::GetRoot() - возвращает корень (статический метод),
.GetAncestor(N) - возвращает предка N-ого уровня,
.GetDescendant(HID1, HID2) - позволяет вставить узел до HID2, после HID1 или между ними.
.GetLevel() - возвращает глубину узла в дереве,
.IsDescendantOf(HID1) - возвращает true, если элемент (this) является потомком HID1,
.Parse(STR) - переводит STR в HIERARCHID,
.GetReparentedValue(HID1, HID2) - переносит элемент от элемента HID1 к элементу HID2,
.ToString() - преобразует HIERARCHID в текст.

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

Отправить комментарий