Использование LIMIT в рекурсивном общем табличном выражении не допускается в MySQL - PullRequest
2 голосов
/ 17 июня 2020

Моя цель - построить дерево с использованием новейшего метода MySQL WITH RECURSIVE.

Моя таблица называется categories и имеет 2 строки. ID и parentID row.

Таблица моих категорий:

 . ID . | ParentID   
--------|----------
 . 1  . | null  
 . 2  . |  1
 . 3  . |  1  
 . 4  . |  1
 . 6  . |  1
 . 7  . |  1
 . 8  . |  1
 . 9  . |  1
 . 10 . |  1
 . 11 . |  13
 . 12 . |  14
 . 13 . |  12     
 .... . | ...

с идентификаторами от 2 до 9, имеют одного и того же родителя, который является родителем с ID = 1. Это то, что Я пытаюсь ограничить, указав «LIMIT 5» во втором запросе SELECT моего рекурсивного общего табличного выражения.

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

+ Item X .............. (level 1)       
  + Item Y .............. (level 2)  
  + Item Y .............. (level 2)   
  + Item Y .............. (level 2) 
  + .... LIMIT to 5 Items 
+ Item X
    + Item X
      + Item X
         + Item X
             + Item X  
+ Item X

Это мой mySQL Recursive Common Table Expression Запрос с предложением LIMIT, вызывающим проблему:

WITH RECURSIVE cte AS
(
  SELECT ID, 0 AS depth, CAST(ID AS CHAR(200)) AS path
    FROM categories WHERE parentID = 1
  UNION ALL
  SELECT c.ID, cte.depth+1, CONCAT(cte.path, ',', c.ID)
    FROM categories c 
    JOIN cte ON cte.ID = c.parentID
    WHERE FIND_IN_SET(c.ID, cte.path)=0 AND depth <= 10
    LIMIT 5
)

 SELECT * FROM cte

Логически я ожидал отсортировать свою проблему, используя предложение LIMIT в вторая часть Select выражения CTE, чтобы ограничить количество строк, возвращаемых вторым оператором SELECT. Но это дает мне ошибку:

This version of MySQL doesn't yet support 'ORDER BY / LIMIT over UNION in recursive Common Table Expression'

Обратите внимание, что я использую MySQL версии 8.0 +. Я так понимаю ошибка очевидна. Но как насчет того, чтобы у меня 1 миллион детей младше одного родителя? Это приведет к зависанию системы!

Я буду очень признателен за обходной путь.

Спасибо.

1 Ответ

0 голосов
/ 17 июня 2020

Если я правильно следил за вами, row_number() может делать то, что вы хотите. Идея состоит в том, чтобы перечислить categories строк в рекурсивной части, а затем отфильтровать их по 5 верхним:

with recursive cte as (
    select id, 0 as depth, cast(id as char(200)) as path
    from categories 
    where parentid = 1
    union all
    select c.id, cte.depth+1, concat(cte.path, ',', c.id)
    from cte
    inner join (
        select c.*, row_number() over(partition by c.parentid order by c.id) rn
        from categories c 
    ) c on cte.id = c.parentid
    where find_in_set(c.id, cte.path) = 0 and depth <= 10 and c.rn <= 5
)
select * from cte

Вы можете немного оптимизировать это, предварительно отфильтровав набор данных:

with recursive 
    cats as (
        select *
        from (
            select c.*, row_number() over(partition by parentid order by id) rn
            from categories c 
        ) t
        where rn <= 5
    ),
    cte as (
        select id, 0 as depth, cast(id as char(200)) as path
        from cats 
        where parentid = 1
        union all
        select c.id, cte.depth+1, concat(cte.path, ',', c.id)
        from cte
        inner join cats c on cte.id = c.parentid
        where find_in_set(c.id, cte.path) = 0 and depth <= 10 and c.rn <= 5
    )
select * from cte
...