SQL - Как хранить и перемещаться по иерархиям? - PullRequest
46 голосов
/ 02 сентября 2008

Какими способами вы пользуетесь для моделирования и получения иерархической информации в базе данных?

Ответы [ 9 ]

31 голосов
/ 19 сентября 2008

Мне нравится модифицированный алгоритм обхода дерева предзаказа. Этот метод позволяет очень легко запрашивать дерево.

Но вот список ссылок на тему, которые я скопировал с веб-страницы разработчиков Zend Framework (PHP) (размещено там Автором: Laurent Melmoux, 05 июня 2007 15:52).

Многие ссылки не зависят от языка:

Существует 2 основных представления и алгоритма для представления иерархических структур с базами данных:

  • вложенный набор, также известный как модифицированный алгоритм обхода дерева предзаказа
  • модель списка смежностей

Это хорошо объяснено здесь:

Вот еще несколько ссылок, которые я собрал:

список смежных моделей

вложенный набор

1069 * в виде графиков *

Классы:

Вложенные множества DB Tree Adodb

Модель посещения ADOdb

PEAR :: DB_NestedSet

PEAR :: Tree

nstrees

14 голосов
/ 07 сентября 2008

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

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

10 голосов
/ 02 сентября 2008

Каков наилучший способ представления иерархии в базе данных SQL? Общая портативная техника?

Давайте предположим, что иерархия в основном читается, но не полностью статична. Допустим, это семейное древо.

Вот как этого не делать:

create table person (
person_id integer autoincrement primary key,
name      varchar(255) not null,
dob       date,
mother    integer,
father    integer
);

И вставка данных так:

person_id   name      dob       mother father  
1           Pops      1900/1/1   null   null  
2           Grandma   1903/2/4   null   null  
3           Dad       1925/4/2   2      1  
4           Uncle Kev 1927/3/3   2      1
5           Cuz Dave  1953/7/8   null   4
6           Billy     1954/8/1   null   3

Вместо этого, разделите ваши узлы и ваши отношения на две таблицы.

create table person (
person_id integer autoincrement primary key,
name      varchar(255) not null,
dob       date
);

create table ancestor (
ancestor_id   integer,
descendant_id integer,
distance      integer
);

Данные создаются следующим образом:

person_id   name      dob       
1           Pops      1900/1/1  
2           Grandma   1903/2/4   
3           Dad       1925/4/2   
4           Uncle Kev 1927/3/3
5           Cuz Dave  1953/7/8   
6           Billy     1954/8/1   

ancestor_id  descendant_id  distance
1            1              0
2            2              0
3            3              0
4            4              0
5            5              0
6            6              0
1            3              1
2            3              1
1            4              1
2            4              1
1            5              2
2            5              2
4            5              1
1            6              2
2            6              2
3            6              1

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

У кого есть бабушка и дедушка?

select * from person where person_id in 
    (select descendant_id from ancestor where distance=2);

Все ваши потомки:

select * from person where person_id in 
    (select descendant_id from ancestor 
    where ancestor_id=1 and distance>0);

Кто такие дяди?

select decendant_id uncle from ancestor 
    where distance=1 and ancestor_id in 
    (select ancestor_id from ancestor 
        where distance=2 and not exists
        (select ancestor_id from ancestor 
        where distance=1 and ancestor_id=uncle)
    )

Вы избегаете всех проблем присоединения таблицы к себе через подзапросы, общее ограничение - 16 подзапросов.

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

9 голосов
/ 02 сентября 2008

Я должен не согласиться с Джошем. Что произойдет, если вы используете огромную иерархическую структуру, такую ​​как организация компании. Люди могут присоединиться / покинуть компанию, изменить строки отчетности и т. Д. Поддержание «расстояния» было бы большой проблемой, и вам пришлось бы поддерживать две таблицы данных.

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

--Create table of dummy data
create table #person (
personID integer IDENTITY(1,1) NOT NULL,
name      varchar(255) not null,
dob       date,
father    integer
);

INSERT INTO #person(name,dob,father)Values('Pops','1900/1/1',NULL);  
INSERT INTO #person(name,dob,father)Values('Grandma','1903/2/4',null);
INSERT INTO #person(name,dob,father)Values('Dad','1925/4/2',1);
INSERT INTO #person(name,dob,father)Values('Uncle Kev','1927/3/3',1);
INSERT INTO #person(name,dob,father)Values('Cuz Dave','1953/7/8',4);
INSERT INTO #person(name,dob,father)Values('Billy','1954/8/1',3);

DECLARE @OldestPerson INT; 
SET @OldestPerson = 1; -- Set this value to the ID of the oldest person in the family

WITH PersonHierarchy (personID,Name,dob,father, HierarchyLevel) AS
(
   SELECT
      personID
      ,Name
      ,dob
      ,father,
      1 as HierarchyLevel
   FROM #person
   WHERE personID = @OldestPerson

   UNION ALL

   SELECT
    e.personID,
      e.Name,
      e.dob,
      e.father,
      eh.HierarchyLevel + 1 AS HierarchyLevel
   FROM #person e
      INNER JOIN PersonHierarchy eh ON
         e.father = eh.personID
)

SELECT *
FROM PersonHierarchy
ORDER BY HierarchyLevel, father;

DROP TABLE #person;
4 голосов
/ 02 сентября 2008

К сведению: SQL Server 2008 вводит новый тип данных HierarchyID для такого рода ситуаций. Дает вам контроль над тем, где в «дереве» находится ваш ряд, как по горизонтали, так и по вертикали.

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

Oracle: ВЫБРАТЬ ... НАЧАТЬ С ... ПОДКЛЮЧИТЬ ПО

Oracle имеет расширение SELECT, которое позволяет легко извлекать данные из дерева. Возможно, SQL Server имеет какое-то подобное расширение?

Этот запрос будет проходить таблицу, в которой отношение вложенности хранится в столбцах parent и child .

select * from my_table
    start with parent = :TOP
    connect by prior child = parent;

http://www.adp -gmbh.ch / ора / SQL / connect_by.html

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

Я предпочитаю сочетание техник, используемых Джошем и Марком Харрисоном:

Две таблицы, одна с данными Person и другая с иерархической информацией (person_id, parent_id [, mother_id]), если PK этой таблицы - person_id, у вас есть простое дерево только с одним родителем по узлу (который имеет смысл в этом случае, но не в других случаях, таких как учетные записи)

Эта таблица hiarchy может быть пересечена с помощью рекурсивных процедур или, если ваша БД поддерживает ее предложениями типа SELECT ... BY PRIOR (Oracle).

Другая возможность, если вы знаете максимальную глубину данных иерархии, которую вы хотите хранить, - это использовать одну таблицу с набором столбцов на уровень иерархии

1 голос
/ 24 ноября 2008

У нас возникла та же проблема, когда мы реализовали компонент дерева для [fleXive] и использовали подход модели дерева вложенных множеств, упомянутый tharkun из MySQL документов.

В дополнение к быстрому (значительно) ускорению мы использовали подход spreaded , который просто означает, что мы использовали максимальное значение Long для правых границ верхнего уровня, что позволяет нам вставлять и перемещать узлы без пересчета всех левых и правильные значения. Значения для левого и правого каналов рассчитываются путем деления диапазона для узла на 3 и использования inner третьего в качестве границ для нового узла.

Пример кода Java можно увидеть здесь .

0 голосов
/ 02 сентября 2008

Если вы используете SQL Server 2005, тогда эта ссылка объясняет, как получить иерархические данные.

Общие табличные выражения (CTE) могут стать вашими друзьями, когда вы освоитесь с ними.

...