Выбор на основе пути в MySQL - PullRequest
4 голосов
/ 14 июня 2011

У меня есть столбец id, столбец parent и столбец path, который является материализованным путем.

Похоже,

1  | \N | 1  
2  | 1  | 1/2  
3  | 2  | 1/2/3  
4  | 3  | 1/2/3/4  
5  | 3  | 1/2/3/5  
6  | 2  | 1/2/6  
7  | 6  | 1/2/6/7  
8  | 2  | 1/2/8  
9  | 1  | 1/9  
10 | 9  | 1/9/10  
11 | 10 | 1/9/10/11  
12 | 11 | 1/9/10/11/12  
13 | 11 | 1/9/10/11/13  
14 | 11 | 1/9/10/11/14  
15 | 14 | 1/9/10/11/14/15  
16 | 14 | 1/9/10/11/14/16  
17 | 14 | 1/9/10/11/14/17  
18 | 10 | 1/9/10/18  
19 | \N | 19  
20 | 19 | 19\20  
21 | 19 | 19\21

Мне нужно выполнить несколько запросов на основе этой таблицы.

Запросы, которые мне нужно сделать: <ч /> Выбрать всех детей id 9

SELECT * FROM `tester` WHERE 'path' LIKE '%/9/%';  

Будет работать нормально, пока вы не замените идентификатор на 1 или 19, поскольку в начале нет /.

SELECT * FROM `tester` WHERE 'path' LIKE '%1/%';

выберет все строки, где число заканчивается на 1, поэтому 1, 11, 21, 31, 211 и т. Д.

SELECT * FROM `tester` WHERE 'path' LIKE '1/%';

будет работать правильно для строк 1 или 19

Итак SELECT * FROM Тестер WHERE 'path' LIKE '1/%' OR 'path' LIKE '%/1/%';
Это лучшее, что я могу предложить, какие-либо предложения?

<ч /> Выберите Прямых детей 9, но не дочерних детей
Для этого Select * from тестер where 'parent' = 9; будет работать нормально.

<ч /> выберите совокупное количество детей 9, х уровней глубоко.

Итак, я хочу получить одну строку из level1, level2, level3, ... levelx или x строк, представляющих различные уровни,

Давайте представим, что x равно 3 для этого примера Строки из этого примера будут 9, 8, 6 (4-й уровень, если мы запросим, ​​будет 3)

Есть идеи?

Редактировать

#select count of children of specific node(5) down to a maximum of three levels, do no include the parent
SELECT COUNT(child.id) children, 
LENGTH(REPLACE(child.path, parent.path, '')) - LENGTH(REPLACE(REPLACE(child.path, parent.path, ''), '/', '')) AS LEVEL
FROM `tester` child JOIN `tester` parent ON child.path LIKE CONCAT(parent.path,'%') 
WHERE parent.id  =5 
GROUP BY LEVEL HAVING LEVEL <= 3 AND LEVEL > 0;

<ч /> ** выберите 9 детских идентификаторов до x уровней, с уровнем относительно 9,

Итак, снова для этого примера мы будем использовать 3 в качестве x.

Мы надеемся вернуться

10 | 1
11 | 2
18 | 2
12 | 3
13 | 3
14 | 3 

Опять я в полной растерянности относительно того, как это сделать.

Редактировать:

#select all information, and relative level from parent of children of specific node(5) down to a maximum of three levels, do no include the parent
SELECT child.*, 
LENGTH(REPLACE(child.path, parent.path, '')) - LENGTH(REPLACE(REPLACE(child.path, parent.path, ''), '/', '')) AS LEVEL
FROM `tester` child JOIN `tester` parent ON child.path LIKE CONCAT(parent.path,'%') 
WHERE parent.id  =9 
GROUP BY id HAVING LEVEL <= 3 AND LEVEL > 0;

1 Ответ

1 голос
/ 14 июня 2011

Просто чтобы дать вам понять, эти решения основаны на сравнении строк, не оптимизированы и не могут использовать индексы.Вы должны рассмотреть вопрос о нормализации ваших таблиц по-другому.(См. Управление иерархическими данными в MySQL )

По некоторым вопросам:


Выберите все дочерние элементы идентификатора 9:

Поскольку столбец Path не включает начальные и конечные слэши, вам необходимо объединить их с путем:

SELECT * 
FROM tester
WHERE CONCAT('/', path, '/') LIKE '%/9/%';

выбрать совокупное число детей 9, x уровней глубины:

Нам нужно сгруппировать по количеству слешей в пути минус количество слешей в родительском пути:

SELECT (LENGTH(c.Path) - LENGTH(REPLACE(c.Path, '/', '')))
    - (LENGTH(p.Path) - LENGTH(REPLACE(p.Path, '/', ''))) AS Level,
    COUNT(*)
FROM tester c
    JOIN tester p ON c.Parent = p.ID
WHERE CONCAT('/', path, '/') LIKE '%/9/%';
GROUP BY 1

Для простоты я использовалПриведенный выше запрос отображает все уровни. Если вы хотите ограничить глубину x уровней, используйте предикат WHERE из приведенного ниже запроса.


выберите идентификаторы 9 детей до x уровней,с уровнем относительно 9:

Мы ищем столбец Path до максимального количества уровней, принимая во внимание уровень родителей:

SELECT c.*
FROM tester c
    JOIN tester p ON c.Parent = p.ID
WHERE CONCAT(
    '/',
    SUBSTRING_INDEX(
        Path, 
        '/', 
        (LENGTH(p.Path) - LENGTH(REPLACE(p.Path, '/', ''))) + 4
    ),
'/') LIKE '%/9/%'

Шаги, которые мыпринимают:

  1. Нам нужно выяснить, насколько глубоко родитель, мы можем fпризнайте, что, посчитав косые черты на пути родителя.(LENGTH(p.Path) - LENGTH(REPLACE(p.Path, '/', '')))
  2. Нам нужно добавить 1 к этому числу, поскольку путь с 1 косой чертой имеет глубину 2 уровня.
  3. Мы добавляем x желаемых уровней.
  4. Захватить столбец пути до общего уровня (используйте функцию SUBSTRING_INDEX).
  5. Добавьте начальную и конечную косую черту.
  6. Найдите последнюю строкудля 9.
...