УДК 519.683 : 004.62
ПРОЕЦИРОВАНИЕ ИЕРАРХИЧЕСКИХ СТРУКТУР РЕЛЯЦИОННЫХ ДАННЫХ В ОБЪЕКТНУЮ МОДЕЛЬ ПРИЛОЖЕНИЯ
© 2009 г. А.В. Маликов, Д.К. Пархоменко, Ю.В. Гулевский
Северо-Кавказский государственный North Caucasus State
технический университет, г. Ставрополь Technical University, Stavropol
Рассматриваются методы представления иерархических структур данных. Предлагается комплексная модель организации взаимодействия между базой данных, содержащей иерархическую структуру, и конечным пользователем. Определяются достоинства и недостатки практического применения предложенной модели.
Ключевые слова: реляционные базы данных; иерархические структуры данных; интерфейс, программирование.
Methods for hierarchical data representation are suggested. Model of interconnection between the database, containing hierarchical structure and the end user is suggested. Advantages and drawbacks of suggested model's practical use are analyzed.
Keywords: relation databases; hierarchical data structures; tree data structures; interface; programming.
Введение
В окружающем нас мире мы постоянно сталкиваемся с иерархическими структурами данных. К таким структурам относятся, например, предприятия и их филиалы, составные части механизмов, классификация товаров, иерархия должностей и т.п. В общем случае все сводится к моделированию многоуровневой связи «главный - подчиненный», «предок - потомок», «общий - частный». Любая группа объектов с такими связями организована в виде иерархической структуры данных. В связи с этим разработчики программных систем часто сталкиваются с ситуациями, в которых применение иерархической структуры для моделирования предметной области является оптимальным решением.
В реляционных базах данных иерархические структуры данных относительно просто создаются и поддерживаются. К наиболее известным моделям относятся:
1. «Модель списка смежных вершин» (adjacency list model). Как известно из теории, граф можно представить в виде матрицы, где на пересечении i-й строки и j-го столбца стоит 1, если между узлами (вершинами) графа с номерами i и j есть связь (ребро, дуга), или 0 в противном случае. Такая абстракция называется матрицей смежности. Матрица смежности может быть также представлена в виде списка (множества) пар с номерами (идентификаторами, кодами) вершин по принципу: есть пара - есть связь, нет пары - нет связи. Корневые вершины отличаются от других пар пустой (NULL) ссылкой на предка [1].
2. «Множественная модель дерева» (nested-set model of trees). Каждой вершине графа ставится в соответствие пара целочисленных значений {left, right), такая, что, если вершина u является по-
томком вершины v, то leftv < leftu, rightu < rightv .
Для вычисления значений {lefti,righti), i = 1...|v|
используется специальный алгоритм обхода и нумерации вершин графа [2].
3. Модели материализованного пути (materialized path). Относительно молодое направление, при котором используется специальный ключ, сопоставляемый каждой вершине дерева и содержащий в себе информацию обо всех вершинах по пути из корня дерева в саму вершину. Так как путь из корня дерева в вершину единственен, то это позволяет сопоставить каждой вершине единственный уникальный ключ или уникальный набор идентифицирующих вершину значений [3, 4].
Еще один способ хранения иерархических структур, который частично можно отнести к моделям материализованного пути, был предложен авторами данной статьи и подробно изложен в [5].
Каждая из моделей имеет свои достоинства и недостатки, и ее выбор зависит от конкретной задачи [1]. Но помимо выбора способа хранения иерархической структуры данных разработчику необходимо организовать взаимодействие между базой данных, содержащей иерархическую структуру данных и клиентским приложением.
Модель интерфейса между базой данных, содержащей иерархическую структуру данных, и клиентским приложением
Архитектура модели подразумевает 3 уровня:
- уровень базы данных;
- слой доступа к данным;
- объектная модель приложения.
Рассмотрим подробнее каждый уровень.
Уровень базы данных
На данном уровне находится система управления базой данных, содержащая иерархическую структуру данных. В качестве модели хранения иерархии предлагается использовать модель, описанную в [5].
Пусть существует некоторая предметная область с определенной структурой. В общем виде взаимосвязи между сущностями и объектами предметной области можно описать орграфом. Орграфу сущностей ставится в соответствие ориентированная сеть. С целью подмены ориентированной сети сущностей деревом вводится бинарное отношение ° - порядок обхода сущностей. Бинарное отношение ° строится на основе отношения непосредственной достижимости ^ на графе сущностей предметной области:
1. Пусть для сущностей Ai, i = 1... k , где k - количество сущностей ориентированной сети, на которые ссылается сущность B , справедливо B ^ Ai, тогда A1 о A2,A2о Aз,...,Ak °B . Следствием данного правила является факт существования наименьшего элемента A' бинарного отношения о; сущность А является одним из стоков ориентированной сети.
2. Пусть для двух произвольных сущностей An, Am и сущности B0 уровня 0 ориентированной сети
справедливо B0 ^ Ап , B0 ^ ^ , Ап * К , Ап ^ A' ,
но не существует An ^ Am, где отношение достижимости ^ - есть рефлексивно-транзитивное замыкание отношения ^ , тогда An о Am, Am о B0.
Согласно данным правилам всякой ориентированной сети сущностей можно сопоставить лес деревьев. Теперь сопоставим каждому атомарному значению предметной области некоторый ключ 1еАКеу .
Пусть для некоторого атомарного значения u сущности Au существует множество значений
{у1,у2,...,vm}, из которых достижима u и сущности которых находятся согласно порядку обхода перед сущностью Au. Множество v2,...,vm} связывается
отношением v1 ° v2 о... о vm так, что, если i, j el...m и 3 (vi о vjj, то отношение vj о vi запрещается и заменяется на vi о vj. Каждой вершине vi, для которой рассчитываемое значение leftKey, не существовало ранее, назначается
leftKeyv = rightreplicate(a,l) + IDvi j,lj +... +
+right(ilreplicate(a,l) + IDv j,lj,
i e1...m , где leftKeyv - ключ leftKey вершины vi,
a - заранее выбранный символьный разделитель, l -длина соответствующего каждому уровню дерева фрагмента ключа, IDv- идентификатор vi, "+" -
операция конкатенации строк, replicate( x1, x2) -двухместная функция формирования символьной строки длины x2 из символов x1, right (x1, x2 j -
двухместная функция, возвращающая х2 правых символов строки х1. Затем достраивается бинарное отношение о: v1 о v2 о... о vm о u , а u назначается ключ leftKeyu = leftKeyv + +right ((replicate(a,l) + Ю11),l) .
С помощью такого ключа можно легко получить необходимое поддерево, так как ключи 1еАКеу всех зависимых вершин будут содержаться в интервале (1еАКеу, 1еАКеу + "Ь"), где "Ь" - некоторый разделитель.
На уровень базы данных поступает запрос об извлечении поддерева данных для некоторого ключа 1еАКеурагеп(. Запрос содержит непосредственно сам
ключ 1еАКеурагеп(, а также может содержать информацию о сущностях, значения которых необходимо получить. Согласно этому запросу на данном уровне всегда будет сформировано две реляционные таблицы. Одна из таблиц представляет собой набор атомарных значений предметной области, ключи 1еАКеу, которых вложены в ключи 1еАКеурагеп(. Вторая таблица отражает взаимосвязи между этими значениями.
Слой доступа к данным
Слой доступа к данным представляет собой уровень абстракции между реляционным представлением иерархической структуры данных в базе данных и объектной моделью приложения. Этот слой - программная прослойка межу сервером БД и клиентским приложением, которая может располагаться либо в клиентском приложении, либо на отдельном сервере.
Функции данной прослойки:
1. Абстрагирование от метода реализации иерархической структуры;
2. Кэширование поддерева значений предметной области;
3. Повышение работоспособности системы в случае изменения структуры предметной области.
В общем случае, способ реализации иерархической структуры на уровне базы данных может быть различным и в зависимости от конкретной СУБД детали реализации могут отличаться. Использование данной прослойки позволяет абстрагироваться от способа представления иерархической структуры и деталей ее реализации, унифицируя доступ к ней. То есть при работе с предметной областью программист обращается не напрямую к СУБД, а использует функциональность, предоставленную слоем доступа к данным.
При работе с иерархической структурой данных пользователю только в очень редких случаях необходимо все дерево значений предметной области. Поэтому, с точки зрения объема занимаемой памяти и количества обращений к СУБД, более оптимально, если объектная модель иерархической структуры данных будет оперировать только с необходимым ей поддеревом значений, загруженным единовременно и кэшированным для дальнейшего использования.
При изменениях в структуре предметной области (добавление/удаление сущностей, находящихся между двумя другими сущностями) функциональность слоя доступа к данным снижает вероятность потери работоспособности приложения. На данном уровне можно предусмотреть механизм, который будет либо игнорировать значения и связи новой сущности, либо организовывать связи между двумя сущностями с учетом того, что между ними раньше находилась третья, удаленная сущность. Данный механизм основывается на использовании ключа leftKey. Например, пусть «Сущность 2» зависит напрямую от «Сущность 1». И, соответственно, все ключи leftKey «Сущность 2» вложены в ключи leftKey «Сущность 1». Если в результате изменения предметной области между «Сущность 1» и «Сущность 2» появилась «Сущность 3», то, несмотря на это, ключи leftKey для «Сущность 2» по-прежнему будут вложены в ключи leftKey «Сущность 1». То же самое произойдет и в случае удаления.
Объектная модель приложения
Для полноценного использования иерархической структуры данных определенной предметной области, пользователю (в данном случае разработчику конкретной программной системы) необходимо предоставить программный интерфейс (API) для взаимодействия с имеющейся иерархией сущностей и их атомарных значений. Решая данную проблему, разработчик API обладает определенной свободой, так как существует немало способов представления древовидных структур в памяти с использованием языков программирования, поддерживающих объектно-ориентированную парадигму.
Реализация объектной модели поддерева подразумевает ряд шагов:
1. Программирование вспомогательных классов.
2. Программирование классов-сущностей предметной области.
Объектная модель приложения представляет собой дерево, узлами которого являются объекты в памяти, инкапсулирующие запись в базе данных (кортеж), относящуюся к определенной сущности предметной области. Каждый такой объект содержит в себе список дочерних узлов, образуя, таким образом, древовидную структуру. Надо отметить, что объекты узлов строго типизированы и тип каждого объекта соответствует классу сущности, к которой этот объект относится.
В силу специфики реляционного представления иерархии, для каждого атомарного значения предметной области необходимо хранить в памяти его уникальный ключ, ключ записи (кортежа), к которой он принадлежит, а также символьный ключ leftKey используемый при выборке. Таким образом, для хранения атомарного значения нет возможности использовать простые типы данных, такие как string, int или double. Для решения этой проблемы предлагается создать набор вспомогательных классов-оберток, инкапсулирующих в себе само атомарное значение со всей сопутствующей информацией: public class TypedValue<T> : ITypedValue public long Id { get; set; } public long IdTuple { get; set;} public T Value { get; set; }
Здесь Id - уникальный идентификатор значения, IdTuple - уникальный идентификатор записи, Value -само значение.
Для представления связи между атомарными значениями также необходимо создать специальный класс-обертку, инкапсулирующий всю необходимую информацию:
public class ParentValue<T> : IParentValue where T : Tuple
public Tuple FirstParent { get; set; } public T Value { get; set; }
Здесь Value - ссылка на родительское значение. FirstParent - вспомогательный объект для операций модификации дерева значений.
Также необходим набор классов, инкапсулирующих отдельные записи (кортежи), соответствующие сущностям предметной области. Внутри каждый такой класс содержит набор свойств, определяющих значения атрибутов и связи между значениями.
public long Id { get; set; }
public abstract IEnumerable<Tuple> Children { get; } public abstract IEnumerable<Tuple> Parents { get; } public abstract IEnumerable<ITypedValue> Values { get; } public class Term : Tuple
private TypedValue<string> Name { get; set; }
private TypedValue<int> WeekCount { get; set; }
private TypedValue<int> WeekStart { get; set; }
public ParentValue<YearOfStudy> ParentYearOfStudy { get; set; }
public ChildList<WeekBlock> WeekBlocks
get { return weekBlocks; }
public override IEnumerable<Tuple> Children
get { return weekBlocks.Cast<Tuple>(); }
public override IEnumerable<Tuple> Parents
get { return new Tuple[] { ParentYearOfStudy.Value }; }
public override IEnumerable<ITypedValue> Values
get { return new ITypedValue[] { name, weekStart, weekCount };
Здесь класс Tuple - абстрактный класс, инкапсулирующий кортеж, где Id - идентификатор кортежа. Children, Parents, Values - списки дочерних, родительских и значений атрибутов кортежа соответственно. В классе Term инкапсулирована запись о конкретной сущности предметной области - семестре, содержащая атрибуты Name (Название семестра), WeekStart (Неделя начала семестра), WeekCount (количество недель в семестре). Также класс содержит строго типизированный список дочерних значений WeekBlocks.
Данная объектная модель обладает возможностью отслеживания и унифицированного сохранения пользовательских изменений в базе данных, что позволяет прозрачно применять изменения для любого поддерева сущностей предметной области.
В силу подобия всех сущностных классов и универсальному представлению информации в виде дерева, существует возможность создания инструмента, способного выполнить за программиста большую часть работы по созданию API управления объектной моделью дерева. С помощью механизма кодогенера-ции и информации об иерархической структуре, содержащейся в базе данных (название сущностей, их атрибутов, и т.д.) можно автоматически создать исходный код строго типизированных классов сущностей, избавляя программиста от объемной рутинной работы по их программированию. Созданный таким образом исходный код программист может дополнять или модифицировать в зависимости от конкретной прикладной задачи. В качестве альтернативы создания отдельного инструмента для этих нужд, его функциональность можно делегировать слою доступа к данным. Подобный подход для реляционных данных реализован в Microsoft ADO.Net и носит название Strongly Typed Datasets.
Схема взаимодействия уровней модели
Для получения поддерева атомарных значений предметной области слою доступа к данным необходим список сущностей предметной области, значения которых будут помещены в объектную модель приложения, а также иерархический ключ leftKeyparent
некоего атомарного значения. Это значение будет являться корнем будущего поддерева.
Далее, используя предоставленную информацию, слой доступа к данным формирует SQL -запрос, и передает его СУБД. В результате выполнения SQL-запроса, прослойка доступа к данным получит поддерево (в реляционном виде) значений-потомков атомарного значения с иерархическим ключом leftKeyparent, которые будут отсортированы по сущностям, и весь набор связей между этими значениями. Затем значения, согласно их связям, будут загружены в объектную модель приложения для дальнейшего использования. Схема данной модели представлена на рис. 1.
Рис. 1. Схема взаимодействия уровней модели
Пример использования интерфейса между базой данных, содержащей иерархическую структуру данных, и клиентским приложением
Пусть существует предметная область «Учебный план», отражающая представление некоторого учебного плана в высшем учебном заведении. Данная предметная область состоит из следующих сущностей: «Год учебного плана», «Семестр», «График», «Вид недели», «Дисциплина плана» и «План». Иерархическая структура данной предметной области показана на рис. 2.
Рис. 2. Предметная область «Учебный план»
Используя правило обхода, представим орграф сущностей предметной области «Учебный план» в виде дерева (рис. 3). Затем согласно правилу формирования ключа 1еАКеу, получим дерево значений предметной области. В таблице представлен вариант наполнения базы данных предметной области «Учебный план».
Предположим, что база данных предметной области «Учебный план» содержит несколько учебных планов, а также включает те данные, которые в качестве примера приведены в таблице. Пользователю необходимо работать в некоторый момент времени с одним учебным планом. Покажем, как будет организовано предоставление пользователю необходимой информации с использованием описанной в данной статье модели.
Табличное представление дерева атомарных значений предметной области «Учебный план»
Сущность Атрибут Атомарное значение Родительское атомарное значение Код значения leftKey
Год учебного плана Год 2007 2007 01 a01
Семестр Номер 1 2007 02 a01a02
Семестр Номер 2 2007 03 a01a03
Вид недели Название Теоретическое обучение 1 04 a01a02a04
Вид недели Название Сессия 1 05 a01a02a05
Вид недели Название Каникулы 1 06 a01a02a06
График Недели 17 Теоретическое обучение 07 a01a02a04a07
График Недели 3 Сессия 08 a01a02a05a08
График Недели 2 Каникулы 09 a01a02a06a09
Дисциплина плана Название История 17 10 a01a02a04a07a10
План Часов 51 История 11 a01a02a04a07a10a11
Необходимо получить поддерево значений учебного плана «2007». Для этого слою доступа к данным будет передан ключ атомарного значения ^Е^ву = а01 и список необходимых сущностей (в данном случае список сущностей не передается ввиду того, что нас интересует все дерево атомарных значений).
Год учебного плана
Семестр
Вид недели
График
Дисциплина плана
План
Рис. 3. Дерево сущностей предметной области «Учебный план»
Слой доступа к данным формирует SQL-запрос:
Select * from Tree where left-Key>'a01' and leftKey<'a01'+'b'.
Сформированный запрос передается на уровень базы данных. После его обработки СУБД вернет в слой доступа к данным поддерева значений предметной области, относящихся к учебному плану года «2007», и набор связей между этими значениями, согласно ключу leftKey.
После загрузки поддерева в слой доступа к данным значения, по мере надобности, помещаются в объектную модель приложения для дальнейшего использования программистом (анализа, модификации, отображения, и т.д.). Следующий код показывает, как создаются объекты класса-сущности «Семестр»:
Выводы
Использование предложенной модели: 1. Позволит упростить написание программного кода приложения, взаимодействующего с базой данных, содержащей иерархическую структуру, так как классы объектной модели приложения, описывающие иерархическую структуру данных на клиента, имеют однотипную структуру и могут быть созданы механизмом кодогенерации;
Subtree sTree = new Subtree(YearOfPlan.LeftKey);
using (DataTable data = sTree.GetValues(YearOfStudy.Id))
if (data.Rows.Count > 0)
foreach (DataRow row in data.Rows)
long tupleKey = row["BizPK"]);
long nameKey = row["BizLK_" + Term.NameAttribute.Name]);
string name = (string)row[Term.NameAttribute.Name]; long weekStartKey = row["BizLK " + Term.WeekStartAttribute.Name]); long weekCountKey = row["BizLK_" + Term.WeekCountAttribute.Name]);
int weekStart = (int)row[Term.WeekStartAttribute.Name];
int weekCount = (int)row[Term.WeekCountAttribute.Name];
Term term = Term.CreateFromDatabase(tupleKey, new TypedValue<string>(name, nameKey, tupleKey), new TypedValue<int>(weekStart, weekStartKey, tupleKey), new TypedValue<int>(weekCount, weekCountKey, tupleKey)); yos.Te rms.Add(t e rm) ;
2. Обеспечит минимальное число обращений к базе данных, содержащей иерархическую структуру. Это обусловлено тем, что слой доступа к данным позволит за один запрос получить все необходимые значения предметной области;
3. Повысит отказоустойчивость системы в некоторых ситуациях изменения предметной области. К таким ситуациям относятся операции «добавление» или «удаление сущностей», находящихся между двумя другими сущностями.
Литература
1. Тарасов С. Иерархические структуры и деревья. Мир ПК. URL:http://www.osp.ru/pcworld/2007/03/4199032.
Поступила в редакцию
2. Joe Celko. Trees in SQL. Some answers to some common questions about SQL trees and hierarchies. URL:http://www. in-telligententerprise.com/001020/celko.jhtml?_requestid=1266295.
3. Roy J. Using the Node Type to Solve Problems with Hierarchies in DB2® Universal Database. IBM Worldwide Sales Support. 2003. URL:http://www.ibm.com/developerworks /db2/ library/techarticle/0302roy/0302roy.html.
4. Tropashko V. Trees in SQL: Nested Sets and Materialized Path. URL:http://www.dbazine.com/oracle/orarticles/ tropashko4.
5. Маликов А.В., Гулевский Ю.В. Математическая модель хранения и эффективной обработки орграфов, представленных в машинном виде // Изв. вузов. Сев.-Кавк. регион. Техн. науки. 2007. № 6. С. 60-65.
20 января 2009 г.
Маликов Андрей Валерьевич - д-р техн. наук, доцент, начальник отдела АСУ, Северо-Кавказский государственный технический университет, г. Ставрополь. Тел. 95-66-58.
Пархоменко Дмитрий Константинович - аспирант, программист отдела АСУ Северо-Кавказский государственный технический университет, г. Ставрополь. Тел. 95-66-58.
Гулевский Юрий Витальевич - аспирант, программист отдела АСУ, Северо-Кавказский государственный технический университет, г. Ставрополь. Тел. 95-66-58.
Malikov Andrei Valerevich - Doctor of Technical Scince, assitant professor, chief of automated systems department, North Caucasus State Technical University, Stavropol. Ph. 95-66-58.
Parkhomenko Dmitriy Konstantinovich - post-graduate student, departament of programmer in automated systems, North Caucasus State Technical University, Stavropol. Ph. 95-66-58.
Gulevskiy Yuriy Vitalevich - post-graduate student, departament of programmer in automated systems North Caucasus State Technical University, Stavropol. Ph. 95-66-58.