Схема базы данных для иерархических групп - PullRequest
6 голосов
/ 22 сентября 2008

Я работаю над дизайном базы данных для иерархии групп, используемой в качестве основы для большей системы Каждая группа может содержать другие группы, а также «устройства» в качестве конечных объектов (ничто не идет ниже устройства).

Используемой базой данных является MS SQL 2005. (Хотя работа в MS SQL 2000 была бы плюсом; решение, требующее MS SQL 2008, к сожалению, пока неосуществимо).

Существуют различные типы групп, и они должны быть динамическими и определяемыми пользователями во время выполнения. Например, типами групп могут быть «customer», «account», «city» или «building», «floor», и каждый тип будет иметь свой набор атрибутов, определяемый пользователем. Также будут применяться бизнес-правила - например, «этаж» может содержаться только под «строительной» группой, и, опять же, они могут быть определены во время выполнения.

Большая часть функциональности приложения обеспечивается за счет запуска отчетов на основе этих групп, поэтому необходим относительно быстрый способ получения списка всех устройств, входящих в определенную группу (и все подгруппы).

Хранение групп с использованием модифицированного метода обхода дерева предварительных заказов имеет преимущество в том, что оно быстрое, но недостатком в том, что оно довольно сложное и хрупкое - если внешние пользователи / приложения изменяют базу данных, существует потенциал для полной поломки. Мы также реализуем слой ORM, и этот метод, кажется, усложняет использование отношений в большинстве библиотек ORM.

Использование общих табличных выражений и «стандартное» отношение id / parentid groups, кажется, является мощным способом избежать запуска нескольких рекурсивных запросов. Есть ли недостатки этого метода?

Что касается атрибутов, каков наилучший способ их хранения? Длинный, узкий стол, относящийся к группе? Должен ли общий атрибут, такой как «имя», храниться в таблице групп вместо таблицы атрибутов (в большинстве случаев имя будет единственным, что требуется для отображения)?

Будут ли проблемы с производительностью при использовании этого метода (предположим, что в среднем 2000 групп по 6 атрибутов в каждой и в среднем 10 одновременно работающих пользователей) на разумном оборудовании, например четырехъядерном Xeon 2 ГГц , 4ГБ оперативной памяти, дисконтирование любых других процессов)?

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

Ответы [ 4 ]

3 голосов
/ 22 сентября 2008

Я бы порекомендовал вам на самом деле создать самый простой в обслуживании способ («стандартную» настройку родитель / потомок) и выполнить хотя бы несколько базовых тестов для него.

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

Принимая 6 атрибутов на группу, 2000 групп и 30 байт / атрибут, вы говорите 360 КБ * ожидаемые элементы / группа - рисунок 400 КБ. Если вы ожидаете иметь 1000 элементов / группу, вы просматриваете только 400 МБ данных - это поместится в память без проблем, и базы данных будут быстрыми при соединениях, когда все данные находятся в памяти .

2 голосов
/ 22 сентября 2008

Общие табличные выражения позволят вам получить список групп с отношениями родитель-потомок. Здесь является примером sproc, использующим CTE для другого приложения. Это достаточно эффективно, но остерегайтесь следующих предостережений:

  1. Если деталь встречается в иерархии более одного раза, она будет сообщаться в каждом месте. Возможно, вам придется постобработать результаты.
  2. CTE несколько туповаты и предлагают ограниченную область для фильтрации результатов в запросе - CTE может появляться не более одного раза в операторе select.

Oracle CONNECT BY несколько более гибок, так как он не накладывает почти столько же ограничений на структуру запроса, сколько CTE, но если вы используете SQL Server, это не подойдет.

Если вам нужно сделать что-нибудь умное с промежуточными результатами, тогда напишите sproc, который использует CTE, чтобы получить необработанный запрос во временную таблицу и работать над этим оттуда. SELECT INTO минимизирует трафик, возникающий при этом. Полученная таблица будет в кеше, поэтому операции с ней будут достаточно быстрыми.

Некоторые возможные физические оптимизации, которые могут помочь:

  • Кластерные индексы на родительском, так что выходит дочерние узлы для родитель использует меньше ввода / вывода.
  • Много оперативной памяти и (в зависимости от размера таблицы спецификаций) 64-разрядный серверы с еще большим объемом оперативной памяти, так что главная таблица спецификаций можно кэшировать в ядре. На 32-битном O / S загрузочный переключатель / 3G - ваш друг, и у него нет реальных недостатков для сервера базы данных
  • DBCC PINTABLE может помочь менеджеру базы данных удерживать таблицу в кеше.

Таблицы кодирования Parent-Attribute Type-Attribute не будут хорошо работать с CTE, так как вы получите комбинаторный взрыв в подсчете строк, если вы включите таблицу атрибутов. Это исключает любую бизнес-логику в запросе, который фильтруется по атрибутам. Было бы гораздо лучше хранить атрибуты непосредственно в записи таблицы спецификаций.

1 голос
/ 22 сентября 2008

Модифицированный предварительный заказ - это, по сути, метод Джо Селко «Вложенные множества». Его книга «Деревья и иерархии ...» охватывает как список смежности, так и NS, с описанием преимуществ и недостатков каждого из них. При правильной индексации CTE списков смежности получает наиболее сбалансированную производительность. Если вы собираетесь читать в основном, NS будет быстрее.

То, что вы, похоже, описываете, это обработчик спецификаций. Хотя у Грэма Биршалла нет M $, у него есть бесплатная книга по DB2 с главой, посвященной обработке иерархии с использованием CTE (синтаксис практически идентичен, IIRC, в том смысле, что синтаксис ANSI принял DB2, а затем M $ принял): http://mysite.verizon.net/Graeme_Birchall/cookbook/DB2V95CK.PDF

1 голос
/ 22 сентября 2008

Предзаказ Tree Traversal очень удобен. Вы можете сделать его устойчивым, обновляя номера обхода с помощью триггеров.

Подобная техника, которую я использовал, заключается в том, чтобы хранить отдельную таблицу (ancestor_id, downndant_id), в которой перечислены все предки и потомки. Это почти так же хорошо, как цифры прохождения предварительного заказа.

Использование отдельной таблицы удобно, потому что, хотя она вводит дополнительное объединение, она убирает сложность в отдельную таблицу.

...