【问题标题】:How to keep the newest rows only in a WITH RECURSIVE statement effectively?如何有效地只在 WITH RECURSIVE 语句中保留最新的行?
【发布时间】:2018-10-09 04:23:10
【问题描述】:

我在表中有一些初始行。我想用递归调用来修改它们。在我的示例代码中,这个函数是一个简单的乘以 2,我想执行 5 次:

WITH RECURSIVE cte (n,v) AS
(

  -- initial values
  SELECT 0,2
  UNION ALL
  SELECT 0,3

  UNION ALL

  -- generator
  SELECT n + 1, v * 2 FROM cte WHERE n < 5
)
SELECT v FROM cte where n = 5;

它有效,但我的问题是它只在查询结束时过滤掉不需要的值。如果我从更多的行开始,它会降低性能,因为我应该在内存中有更多的行。是否可以仅在每次迭代中保留最新值?

SQLFiddle:http://sqlfiddle.com/#!5/9eecb7/6761

【问题讨论】:

  • @TimBiegeleisen 你知道这个代码在 MySQL 8.0 中是可能的,所以你可以通过删除 MySQL 标签来加快速度。
  • 为什么这个问题同时被 MySQL 和 SQLite 标记?
  • @RaymondNijland 因为我正在寻找一种适用于两者的解决方案
  • “因为我正在寻找一种适用于两者的解决方案”-> 在 sqlfiddle.com 上提供带有示例数据的表结构,并将基于该示例数据的预期输出作为 ascii 表数据发布.. i '已经添加了 mysql 8.0 标签,因为 MySQL 8.0 是唯一允许公共表表达式(WITH AS 子句)的 MySQL 版本
  • 我用 sqlfiddle 链接更新了问题。它提供了预期的输出,我只是在寻找更优化的解决方案

标签: mysql sql sqlite common-table-expression mysql-8.0


【解决方案1】:

在 SQLite 中你可以使用OFFSET clause

  • OFFSET 子句,如果它存在并且具有正值 N, 防止将前 N 行添加到递归表中。这 前 N 行仍然由递归选择处理——它们只是 不会添加到递归表中。行数不计入 满足 LIMIT 直到跳过所有 OFFSET 行。

演示:http://sqlfiddle.com/#!5/9eecb7/6804

WITH RECURSIVE cte (n,v) AS
(

  -- initial values
  SELECT 0,2
  UNION ALL
  SELECT 0,3

  UNION ALL

  -- generator
  SELECT n + 1, v * 2 FROM cte WHERE n < 5 LIMIT 1000 OFFSET 10

)
SELECT * FROM cte

| n |  v |
|---|----|
| 5 | 64 |
| 5 | 96 |

在上面的示例中,偏移量计算为初始选择中的初始行数(2 行)乘以迭代次数 (5) => 2*5=10


顺便说一下,在这个具体的例子中,更好的解决方案是计算简单的X * 2^5
(X 乘以 2 到 5 的幂)而不是递归。

【讨论】:

    【解决方案2】:

    在 SQLite 中,CTE 是作为协程实现的(如EXPLAIN 输出所示),因此只有当前行保存在内存中,不会因内存使用而降低性能。

    MySQLdoes not allow LIMIT in the recursive SELECT part。如果我正确解释 WL#3634,8.0 版中的实现总是完全实现递归 CTE。

    所以在 SQLite 中,你不需要做任何事情,而在 MySQL 中,你什么也做不了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-02
      • 2019-11-06
      • 2017-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多