Выборка дерева
Как рекурсивно получить структуру связей родителей в виде дерева одним запросом в MySQL?
id | name | parent_id |
---|---|---|
19 | category1 | 0 |
20 | category2 | 19 |
21 | category3 | 20 |
22 | category4 | 21 |
Согласно этого фидла: http://sqlfiddle.com/#!9/5de2a/1
- tree-select-id.sql
SELECT id, name, parent_id FROM (SELECT * FROM products ORDER BY parent_id, id) products_sorted, (SELECT @pv := '19') initialisation WHERE find_in_set(parent_id, @pv) > 0 AND @pv := concat(@pv, ',', id)
Но если использовать строковые ключи (например, GUID
), то на такой таблице этот запрос вернет пустой результат:
CREATE TABLE products( id CHAR(36) NOT NULL, name VARCHAR(100), parent_id CHAR(36) NOT NULL ); SELECT id, name, parent_id FROM (SELECT * FROM products ORDER BY parent_id, id) products_sorted, (SELECT @pv := 'b10') initialisation WHERE find_in_set(parent_id, @pv) > 0 AND @pv := concat(@pv, ',', id);
Однако если заменить 'b10' на '10', то результаты будут. Дело в том, что
@pv := concat(@pv, ',', id)
будет возвращать false
, если первым символом будет не цифра.
Перепишем, чтобы всегда было true
:
SELECT id, parent_id FROM (SELECT * FROM products ) products_sorted, (SELECT @pv := 'b10') initialisation WHERE find_in_set(parent_id, @pv) > 0 AND ((@pv := concat(@pv, ',', id)) OR 1);
Будет возвращен один результат, а должно быть три. Похоже, что оптимизатор MySQL не вычисляет значение @pv
, так как условие всегда возвращает true
. Это можно проверить:
mysql> SELECT @pv; +------+ | @pv | +------+ | b10 | +------+
Подсунет ему вариант, чтобы он высчитывал его каждый раз:
SELECT id, parent_id FROM (SELECT * FROM products ) products_sorted, (SELECT @pv := 'b10') initialisation WHERE find_in_set(parent_id, @pv) > 0 AND LENGTH(@pv := concat(@pv, ',', id)) > 0;
Готовый вариант выборки дерева по guid
: http://sqlfiddle.com/#!9/e59655/2
Второй вариант.
SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM ( SELECT @Ids := ( SELECT GROUP_CONCAT(`id` SEPARATOR ',') FROM `products` WHERE FIND_IN_SET(`parent_id`, @Ids) ) Level FROM `products` JOIN (SELECT @Ids := 'b10') temp1 WHERE FIND_IN_SET(`parent_id`, @Ids) ) temp2
Получить всех родителей
SELECT T2.* FROM ( SELECT @r AS _id, (SELECT @r := parent_id FROM products WHERE id = _id) AS parent_id, @l := @l + 1 AS lvl FROM (SELECT @r := 'b13', @l := 0) vars, products m WHERE @r <> '') T1 JOIN products T2 ON T1._id = T2.id ORDER BY T1.lvl DESC;