简体   繁体   English

MySQL 中不允许在递归公用表表达式中使用 LIMIT

[英]Use of LIMIT in Recursive Common Table Expression is not allowed in MySQL

My goal is to construct a tree using newest MySQL's WITH RECURSIVE method.我的目标是使用最新的 MySQL 的 WITH RECURSIVE 方法构造一棵树。

My table is called categories which has 2 rows.我的表称为categories ,有 2 行。 The ID and the parentID row . IDparentID row

My Categories Table:我的分类表:

 . 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     
 .... . | ...

IDs from 2 to 9, have the same parent which is parent with ID = 1. This is what I am attempting to Limit by providing a "LIMIT 5" in the second SELECT query of my Recursive Common Table Expression.从 2 到 9 的 ID 具有相同的父级,即 ID = 1 的父级。这就是我试图通过在我的递归公用表表达式的第二个 SELECT 查询中提供“LIMIT 5”来限制的内容。

An optical representation of the above table in a tree would be something like the following: My problem is to limit the number of children of the same level (marked as Item Y in below illustration).上表在树中的光学表示将类似于以下内容:我的问题是限制同一级别的孩子的数量(在下图中标记为项目 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

This is my mySQL Recursive Common Table Expression Query with the LIMIT clause causing the problem:这是我的 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

Logically I was expecting to sort my problem by using a LIMIT clause in the second Select part of the CTE to constrain the number of rows returned by the second SELECT statement.从逻辑上讲,我希望通过在 CTE 的第二个 Select 部分中使用 LIMIT 子句来限制第二个 SELECT 语句返回的行数来对我的问题进行排序。 But it gives me an error:但它给了我一个错误:

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

Note that I am using MySQL version 8.0 +.请注意,我使用的是 MySQL 版本 8.0 +。 I understand the error is clear.我明白错误很明显。 But what about if I have 1 million children below the same parent?但是,如果我在同一个父母之下有 100 万个孩子呢? It will freeze the system !它会冻结系统!

I will greatly appreciate a workaround.我将非常感谢一种解决方法。

Thank you.谢谢你。

If I followed you correctly, row_number() can do what you want.如果我正确地跟着你, row_number()可以做你想做的事。 The idea is to enumerate the categories rows in the recusive part, then filter on the top 5:这个想法是枚举递归部分中的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

You can optimize this a little by pre-filtering the dataset:您可以通过预过滤数据集对此进行一些优化:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM