如何有效地在WITH RECURSIVE语句中保留最新的行?

时间:2018-04-28 14:03:59

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

我在表格中有一些初始行。我想用递归调用修改它们。在我的示例代码中,这个函数是一个简单的乘以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

2 个答案:

答案 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)=&gt;的初始行数。 2×5 = 10

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

答案 1 :(得分:1)

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

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

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