В первой части статьи были размышления об иерархии. Здесь постараюсь раскрыть вопросы практической реализации. В рамках этой статьи попробуем реализовать иерархию, похожую на каталог Яндекса. Причем для краткости положим, что каждый элемент должен принадлежать двум иерархиям: Тема, География.

 

Постановка задачи

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

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

 

Первое и неправильное решение

Структура таблицыПервое что приходит в голову программисту, который мало работал с реляционными СУБД, организовать некую структуру с полями ID , ParentID . Тогда, как считает программист, можно будет создавать только одну сущность и поэтому сэкономить на разработке.

Программист знает, что в Аксапте есть объект treeView , программист прочитал статью Using trees in forms и посмотрел на обучающие формы tutorial_FormTreeControl и tutorial_Form_TreeDatasource. Кроме того, именно так иерархия реализована в 1С, а, как правило, российские заказчики под иерархией подразумевают «сделать как в 1С» (см. Иерархический справочник и Axapta. Часть 1: Размышления).

Программист решает реализовать иерархию именно таким образом и… делает чудовищную ошибку.

 

Какие сложности возникают при реализации иерархических справочников на реляционных СУБД

Да, такая структура хорошо работает, все элементы полностью хранится в памяти. В памяти переход к родителю выполняется быстро. Если же элементов очень много, то переход к родителю или поиск всех подчиненных элементов означает что… будет выполняться новый запрос. Новый запрос – это тормоза, это трафик между клиентом и сервером, это повышенные требования к клиенту. В идеале в клиент-серверной архитектуре клиент должен получать только минимально-необходимые для отображения данные. Не больше.

Типичные реляционные СУБД (например, MS SQL) принципиально не умеют работать с иерархиями (См. например Введение в базы данных). Это значит, что в реляционных СУБД обойти дерево, структура которого предсталена в виде пары ID/ParentID, за один запрос принципиально невозможно. Это значит, что в типичных реляционных СУБД работа с деревом заведомо будет выполняться менее оптимально, нежели работа без дерева.

Отчет с итогами по иерархическим группам Но производительность это не самое главное. Главное, ради чего создаются информационные системы, это отчеты. Отчеты позволяют людям принимать решения. Ввод данных нужен ровно настолько, насколько нужны отчеты.

Так вот, самое главное, что нужно пользователям с появлением иерархии – суммы по группам.

И здесь программиста снова поджидает ЗАСАДА со стороны реляционных СУБД. Они принципиально не умеют возвращать итоги и элементы одновременно! Это значит, чтобы получить отчет, похожий на 1Совский, программист будет вынужден отправить серверу по меньшей мере 2 (два) запроса. Один для получения элементов, другой для получения итогов.

Кроме того, программист должен будет писать кучу кода, для того, чтобы правильно вывести результаты двух запросов на одном листе. А если вспомнить, что в реляционных СУБД для обхода дерева требуется несколько запросов…

По ссылкам можно посмотреть как народ народ мучается и пытается обойти сложности иерархий на реляционных СУБД:

http://rdbms.narod.ru/article/tree/index.html
http://sdm.viptop.ru/lib/sqltrees.html
http://www.ibase.ru/develop.htm#tree
http://www.osp.ru/os/2000/01-02/079.htm
http://support.microsoft.com/default.aspx?scid=KB;EN-US;q248915&
http://www.osp.ru/os/2004/02/062_print.htm
http://www.sql.ru/forum/actualthread.aspx?bid=1&tid=10197
http://www.sql.ru/forum/actualthread.aspx?bid=10&tid=29091
http://www.osp.ru/os/2002/06/050_1_print.htm
http://www.osp.ru/win2000/sql/2001/05/967.htm
http://www.osp.ru/win2000/sql/2001/05/968.htm
http://www.osp.ru/win2000/sql/970.htm
http://sql.ru/forum/actualthread.aspx?bid=36&tid=73104
http://www.sql.ru/forum/actualthread.aspx?bid=1&tid=5961&pg=1
http://habrahabr.ru/blogs/development/46659/
http://habrahabr.ru/blogs/development/47280/

"Помилуйте!", говорят мне обычно в этом месте…

Но ведь 1С работает с иерархией

  – Да какой же вы рыцарь?
– Уж, какой есть.
(С) Шрек

Да, разработчики 1С проделали огромную работу чтобы спрятать от рядового программиста все эти сложности по работе с иерархиями. И это действительно титанический труд, который стоит уважать.

Только есть одно но: производительность. 1С ругают именно за то, что она не использует возможности СУБД, 1С ругают именно за то, что она не является клиент/серверной. Вы делаете производительную систему рассчитанную на большого количество пользователей и большой объем данных. Вы точно хотите идти этим путем?

Обычно в ответ на вопрос о иерархиях в 1С достаточно рассказать о том, какие запросы делает 1С при работе с иерархическими справочниками и итогами по ним. Сейчас же статья о другом, поэтому останавливаться на технологиях работы 1С не буду. Для тех, кто интересуется - отпрофилируйте запросы, которые отсылает 1С серверу при работе с иерархическими справочниками. Вы точно хотите идти этим путем?

Вернемся к задаче

Далее программист начинает думать и жаловаться на жизнь постановщику. Постановщик начинает думать над задачей. После продолжительного раздумья становится понятно, что на самом деле:

  1. иерархия – это способ фильтрации данных (См. Иерархический справочник и Axapta. Часть 1: Размышления)
  2. вся проблема в том, что изначально мы пытались сделать универсальное решение

Поясню второй пункт таблица с парой ID/ParentID является универсальной реализацией дерева. Это решение не накладывает никаких ограничений на глубину, расположение элементов, на способ доступа и т.п.

Сложности с реализацией на реляционных СУБД возникают именно из-за универсальности решения.

Пожалуйста, остановитесь на этом месте. Подумайте.

Теперь думаем дальше. На самом деле универсальное решение – замечательная штука. Но это универсальное решение вредит и программисту, и… пользователям. С программистом все понятно – ему сложнее реализовывать эффективное решение. А почему пользователям?

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

Мало того, в подавляющем числе случаев, эти правила создания иерархий уже сформулированы заказчиками! Просто постановщики задач, как правило не хотят думать на эту тему, а просто заставляют программистов делать универсальные деревья. Плохим постановщикам так проще жить! Что же делают хорошие постановщики?

Каталог Яндекса Вернемся к нашей задаче. Задача - повторить каталог Яндекса, каждый элемент должен принадлежать двум иерархиям: тема, география.

Прежде всего, необходимо понять каковы эти иерархии.

Иерархия Тема может содержать четыре уровня:

На самом деле, в Яндексе нигде нет пятого уровня! В каталог яндекса максимум 4хуровневый. Почему? Разработчики яндекса такие неумехи? Нет.

Просто для того, чтобы добраться до 5 уровня, пользователю надо открыть 5 страниц и при этом 5 раз щелкнуть мышкой. Просто глубокие иерархии неудобны для пользователя. Об этом тоже было в первой части. Этот момент и учли разработчики каталога Яндекса.

Иерархия География может содержать только три уровня:

Еще раз хочу подчеркнуть: ограничения на количество уровней не технические, а принципиальные. Эти ограничения присущи самой задаче. Да, можно довести географиию до улиц и дома. Но кто будет предоставлять такие данные? Будут ли пользователи использовать дома при поиске?

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

Таким образом, постановщик должен сформулировать не только ТЗ на разработку, но и выявить принципиальные ограничения, ограничения присущие самой задаче. В данном случае, постановщик должен отметить, что:

Второе решение

Второе решение совершенно очевидно для тех, кто работал с реляционными СУБД:

Каждый уровень иерархии делать отдельной таблицей.

 

Окончание следует...

Буду рад Вашим замечаниям и предложениям.
Мазуркин Сергей, mazzy@mazzy.ru.