MySQL,递归CTE,它是如何以这样的语法形式工作的?

uqxowvwt  于 2023-01-12  发布在  Mysql
关注(0)|答案(1)|浏览(124)

常见的递归CTE如下所示:

WITH RECURSIVE
cte AS (
  SELECT 1 id
  UNION ALL
  SELECT id + 1 FROM cte WHERE id < 1000
)
SELECT COUNT(*) FROM cte;

该表格在参考手册中有详细描述。
但是使用CTE的另一种合成形式可以产生相同的输出:

WITH RECURSIVE
cte AS (
  SELECT 1 id
  UNION ALL
  SELECT id + 1 FROM cte LIMIT 1000
)
SELECT COUNT(*) FROM cte;

RM中未描述该替代形式。此外,它与RM中描述的下一部分不匹配:
递归部分的每次迭代仅对上一次迭代生成的行进行操作。如果递归部分有多个查询块,则每个查询块的迭代将按未指定的顺序进行调度,并且每个查询块对上一次迭代生成的行或自上一次迭代结束后由其他查询块生成的行进行操作。
递归部分的每次迭代都产生一行。递归部分中没有任何内容会导致在递归的下一步不会创建新行。
但是递归仍然结束,并且总行数对应于LIMIT值。
递归CTE如何以这样的语法形式工作?
DEMO fiddle

更新-从备注中复制。

原因是LIMIT应用于UNION语句,而不是单个查询(除非使用括号)。-lemon
这看起来很明显,但是我找不到RM在哪里描述了整个的,组合的,结果正在被研究。
还有一件事-如果CTE递归停止,则会生成第1001行,然后使用LIMIT将其丢弃,这不会导致新行并停止递归...但我找不到确切的原因,为什么使用LIMIT确定性地丢弃在最后一次迭代中创建的行。

t3psigkw

t3psigkw1#

您可以在您正在查看的同一参考手册页面中找到递归查询上下文中LIMIT用法的参考。
在MySQL 8.0.19之前,递归CTE的递归SELECT部分也不能使用LIMIT子句。在MySQL 8.0.19中取消了这一限制,现在在这种情况下支持LIMIT以及可选的OFFSET子句。对结果集的影响与在最外层SELECT中使用LIMIT相同,但也更有效。因为将它与递归SELECT一起使用会在生成请求数量的行后立即停止生成行。
这些约束不适用于递归CTE的非递归SELECT部分。
总而言之,如果将LIMIT子句应用于每个递归步骤,则它在递归查询中没有意义(只要每次迭代可能带来的行数小于定义的限制,而不会停止递归本身),这是MySQL 5中没有考虑到它的原因。开发人员决定将其包含在v8中,以便仅允许限制递归步骤的全部行数,因此不作为递归本身的一部分进行迭代。
这方面的另一个提示是,如果您尝试将LIMIT子句包含在圆括号内,则会将LIMIT子句专门归入递归查询部分,如下所示:

WITH RECURSIVE
cte AS (
  SELECT 1 id
  UNION ALL
  (SELECT id + 1 FROM cte LIMIT 1000)
)
SELECT COUNT(*) FROM cte;

你会得到这样的错误被解雇:
此版本的MySQL尚不支持“公用表表达式的递归查询块中的ORDER BY / LIMIT / SELECT DISTINCT”

相关问题