Вложенный запрос 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 тут:

  1. сделать выбор (WHERE), что приводит к трем строкам в неопределенном порядок:

    ("Food")
    ("Yellow")
    ("Fruit")
    
  2. агрегировать результат в одну строку (неявный GROUP BY) для того, чтобы иметь возможность использовать агрегатную функцию:

    (("Food","Yellow", "Fruit"))
    
  3. запустить агрегатную функцию (GROUP_CONCAT(title, ORDER BY link)) на нем. Т. е. заказать по ссылке, а затем объединить:

    ("Food,Fruit,Yellow")
    
  4. и теперь, наконец, он сортирует этот результат (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 () функция будет упорядочивать выходную строку в порядке возрастания значения столбца. Он не будет рассматривать вас явным порядком по предложению.

если вы проверяете вывод первого запроса, который возвращает данные в порядке возрастания заголовок