Вложенный запрос Set для извлечения всех предков каждого узла
у меня есть запрос MySQL, который, как я думал, работал нормально, чтобы получить всех предков каждого узла, начиная с верхнего узла, вплоть до его непосредственного узла. Однако, когда я добавил 5-й уровень к вложенному набору, он сломался.
Ниже приведены примеры таблиц, запросов и скрипок SQL:
Четырехуровневый Вложенный Набор:
CREATE TABLE Tree
(title varchar(20) PRIMARY KEY,
`tree` int,
`left` int,
`right` int);
INSERT Tree
VALUES
("Food", 1, 1, 18),
('Fruit', 1, 2, 11),
('Red', 1, 3, 6),
('Cherry', 1, 4, 5),
('Yellow', 1, 7, 10),
('Banana', 1, 8, 9),
('Meat', 1, 12, 17),
('Beef', 1, 13, 14),
('Pork', 1, 15, 16);
Запрос:
SELECT t0.title node
,(SELECT GROUP_CONCAT(t2.title)
FROM Tree t2
WHERE t2.left<t0.left AND t2.right>t0.right
ORDER BY t2.left) ancestors
FROM Tree t0
GROUP BY t0.title;
возвращенный результат для node Banana
is Food,Fruit,Yellow
- отлично. Вы можете см. здесь Скрипка SQL-4 уровня
когда я запускаю тот же запрос в таблице 5-го уровня ниже, узлы 5-го уровня возвращаются в неправильном порядке:
CREATE TABLE Tree
(title varchar(20) PRIMARY KEY,
`tree` int,
`left` int,
`right` int);
INSERT Tree
VALUES
("Food", 1, 1, 24),
('Fruit', 1, 2, 13),
('Red', 1, 3, 8),
('Cherry', 1, 4, 7),
('Cherry_pie', 1, 5, 6),
('Yellow', 1, 9, 12),
('Banana', 1, 10, 11),
('Meat', 1, 14, 23),
('Beef', 1, 15, 16),
('Pork', 1, 17, 22),
('Bacon', 1, 18, 21),
('Bacon_Sandwich', 1, 19, 20);
возвращенный результат для Bacon_Sandwich
is Bacon,Food,Meat,Pork
это не правильный порядок, он должен быть!--8--> - вы можете увидеть это здесь SQL Fiddle-5 уровней
я не уверен, что происходит, потому что я действительно недостаточно хорошо понимаю подзапросы. Может ли кто-нибудь пролить свет на это?
РЕДАКТИРОВАТЬ ПОСЛЕ РАССЛЕДОВАНИЯ:
Уоу!! Похоже, что писать все это и читать о заказе с GROUP_CONCAT
дали мне вдохновения.
добавлять ORDER BY
фактический GROUP_CONCAT
функция и удаление из конца подзапроса решили проблему. Теперь я получаю Food,Meat,Pork,Bacon
для узла Bacon_Sandwich
SELECT t0.title node
,(SELECT GROUP_CONCAT(t2.title ORDER BY t2.left)
FROM Tree t2
WHERE t2.left<t0.left AND t2.right>t0.right
) ancestors
FROM Tree t0
GROUP BY t0.title;
я до сих пор не знаю, почему. Имея ORDER BY
в конце подзапроса работает для 4 уровнях, но не на 5?!?!
если кто-то может объяснить, в чем проблема и почему движется ORDER BY
исправляет это, я был бы очень благодарен.
2 ответов
сначала важно понять, что у вас есть подразумевается GROUP BY
если вы используете функцию group в операторе, не содержащем предложения GROUP BY, это эквивалентно группированию по всем строкам.
чтобы сделать точку более понятной, я оставлю подзапросы и уменьшу проблему до банана. Банан-это набор [10, 11]. Правильными отсортированными предками являются следующие:
SELECT "banana" as node, GROUP_CONCAT(title ORDER by `left`)
FROM Tree WHERE `left` < 10 AND `right` > 11
GROUP BY node;
на ORDER BY
должен быть в GROUP_CONCAT()
как вы хотите, чтобы функция агрегации сортировать. ORDER BY
внешние сортировки по агрегированным результатам (т. е. результат GROUP_CONCAT()
). Тот факт, что он работал до 4-го уровня-это просто удача. ORDER BY
не влияет на агрегатную функцию. Вы получите те же результаты С или без ORDER BY
:
SELECT GROUP_CONCAT(title)
FROM Tree WHERE `left` < 10 AND `right` > 11
/* ORDER BY `left` */
это может помочь понять, что
SELECT GROUP_CONCAT(title ORDER BY left) FROM Tree WHERE … ORDER BY left
тут:
-
сделать выбор (
WHERE
), что приводит к трем строкам в неопределенном порядок:("Food") ("Yellow") ("Fruit")
-
агрегировать результат в одну строку (неявный
GROUP BY
) для того, чтобы иметь возможность использовать агрегатную функцию:(("Food","Yellow", "Fruit"))
-
запустить агрегатную функцию (
GROUP_CONCAT(title, ORDER BY link)
) на нем. Т. е. заказать по ссылке, а затем объединить:("Food,Fruit,Yellow")
-
и теперь, наконец, он сортирует этот результат (
ORDER BY
). Поскольку это только одна строка, сортировка ничего не меняет.("Food,Fruit,Yellow")
вы можете получить результат, используя JOIN
или SUB-QUERY
.
использование JOIN:
SELECT t0.title node, GROUP_CONCAT(t2.title ORDER BY t2.left) ancestors
FROM Tree t0
LEFT JOIN Tree t2 ON t2.left < t0.left AND t2.right > t0.right
GROUP BY t0.title;
проверить это SQL FIDDLE DEMO
использование подзапроса:
SELECT t0.title node,
(SELECT GROUP_CONCAT(t2.title ORDER BY t2.left)
FROM Tree t2 WHERE t2.left<t0.left AND t2.right>t0.right) ancestors
FROM Tree t0
GROUP BY t0.title;
проверить это SQL FIDDLE DEMO
выход
| NODE | ANCESTORS |
|----------------|-----------------------|
| Bacon | Food,Meat,Pork |
| Bacon_Sandwich | Food,Meat,Pork,Bacon |
| Banana | Food,Fruit,Yellow |
| Beef | Food,Meat |
| Cherry | Food,Fruit,Red |
| Cherry_pie | Food,Fruit,Red,Cherry |
| Food | (null) |
| Fruit | Food |
| Meat | Food |
| Pork | Food,Meat |
| Red | Food,Fruit |
| Yellow | Food,Fruit |
в вашем подзапросе вы использовали ORDER BY
после WHERE
предложение, которое не будет влияет на выход. По умолчанию GROUP_CONCAT () функция будет упорядочивать выходную строку в порядке возрастания значения столбца. Он не будет рассматривать вас явным порядком по предложению.
если вы проверяете вывод первого запроса, который возвращает данные в порядке возрастания заголовок