Какой правильный запрос, чтобы получить всех детей в дереве? - PullRequest
1 голос
/ 25 марта 2010

Допустим, у меня есть следующая структура MySQL:

CREATE TABLE `domains` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`domain` CHAR(50) NOT NULL,
`parent` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MYISAM AUTO_INCREMENT=10 DEFAULT CHARSET=latin1

insert  into `domains`(`id`,`domain`,`parent`) values (1,'.com',0);
insert  into `domains`(`id`,`domain`,`parent`) values (2,'example.com',1);
insert  into `domains`(`id`,`domain`,`parent`) values (3,'sub1.example.com',2);
insert  into `domains`(`id`,`domain`,`parent`) values (4,'sub2.example.com',2);
insert  into `domains`(`id`,`domain`,`parent`) values (5,'s1.sub1.example.com',3);
insert  into `domains`(`id`,`domain`,`parent`) values (6,'s2.sub1.example.com',3);
insert  into `domains`(`id`,`domain`,`parent`) values (7,'sx1.s1.sub1.example.com',5);
insert  into `domains`(`id`,`domain`,`parent`) values (8,'sx2.s2.sub1.example.com',6);
insert  into `domains`(`id`,`domain`,`parent`) values (9,'x.sub2.example.com',4);

На мой взгляд, этого достаточно, чтобы подражать простой древовидной структуре:

            .com
             |             
           example                 
        /          \
      sub1          sub2

ЭСТ

Моя проблема заключается в том, что на sub1.example.com я хочу знать всех потомков sub1.example.com, не используя в моем коде несколько запросов.

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

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

Я немного покопался, а кто-то сделал что-то подобное, но им требовалось использовать функцию в MySQL. Я не думаю, что для чего-то простого нам понадобится целая функция.

Может быть, я просто тупой и не вижу какого-то очевидного решения.

Кроме того, не стесняйтесь изменять структуру.

Ответы [ 4 ]

2 голосов
/ 25 марта 2010

вам следует подумать об использовании вложенных наборов для таких структур данных

см. http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/ для подробной информации о реализации и использовании

1 голос
/ 25 марта 2010

Mysql хорошая статья для вас

Введение

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

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

0 голосов
/ 27 марта 2010

Решение было простым, хотя и спорным по его эффективности.

Я изменил структуру таблицы следующим образом:

CREATE TABLE `domains` (
  `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `domain` CHAR(50) NOT NULL,
  `level` INT(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MYISAM AUTO_INCREMENT=11 DEFAULT CHARSET=latin1

Уровень относится к глубине дерева.

Пример данных:

insert  into `domains`(`id`,`domain`,`level`) values (1,'.com',0);
insert  into `domains`(`id`,`domain`,`level`) values (2,'example.com',1);
insert  into `domains`(`id`,`domain`,`level`) values (3,'sub1.example.com',2);
insert  into `domains`(`id`,`domain`,`level`) values (4,'sub2.example.com',2);
insert  into `domains`(`id`,`domain`,`level`) values (5,'s1.sub1.example.com',3);
insert  into `domains`(`id`,`domain`,`level`) values (6,'s2.sub1.example.com',3);
insert  into `domains`(`id`,`domain`,`level`) values (7,'sx1.s1.sub1.example.com',4);
insert  into `domains`(`id`,`domain`,`level`) values (8,'sx2.s2.sub1.example.com',4);
insert  into `domains`(`id`,`domain`,`level`) values (9,'x.sub2.example.com',3);
insert  into `domains`(`id`,`domain`,`level`) values (10,'t.sx1.s1.sub1.example.com',5);

Итак, допустим, нам дан sub1.domain.com, и мы хотим знать всех его дочерних элементов, запрос довольно прост:

SELECT * FROM domains WHERE domain LIKE "%.sub1.example.com" ORDER BY level;

Конечно, если нам нужен sub1.example.com в нашем наборе результатов, мы можем просто сделать:

SELECT * FROM domains WHERE domain LIKE "%sub1.example.com" ORDER BY level;

Из результирующего набора мы получаем список всех детей, которым дан ребенок.

Удалить дочернего элемента (и всех связанных с ним дочерних элементов) просто и очень похожий запрос

DELETE FROM domains WHERE domain LIKE "%sub1.example.com";

Вставка проста, и для этого нужно всего 2 запроса (при условии, что у пользователя есть выпадающий список и он выбирает родителя):

SELECT level FROM domains WHERE domain = "sub2.example.com";

INSERT INTO domains (domain, level) VALUES ($sub + ".sub2.example.com", $level+1)

Прошу прощения за смешанный синтаксис PHP + MySQL, но вы поняли идею.

0 голосов
/ 26 марта 2010

Списки смежности только помогут вам получить неправильные результаты.

a.d.f и b.d.c делают четыре узла "смежными" с d, но в действительности ни a.d.c, ни b.d.f не существует. Но закрытие списка смежности фактически сделало бы вид, что они это сделают.

Таким образом, ваш запрос действительно должен быть чем-то вроде "... WHERE ENDSWITH (домен, ).

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

Может быть, это можно решить, создав вторую таблицу (домен1, домен2), которая говорит только о том, что «домен1 является поддоменом домена2». Вы обновляете эту таблицу, используя триггеры или sprocs, которые запускаются при каждом обновлении вашей базовой таблицы. Вставка «a.b.c.d» вставляет три строки во второй таблице: (a.b.c.d, b.c.d), (a.b.c.d, c.d), (a.b.c.d, d).

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

EDIT

но есть серьезные проблемы с таким подходом. Если файл a.b.c.d снова будет удален, то должны поступить и другие три строки, если, конечно, еще не было некоторой строки x.b.c.d, которая не была удалена ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...