【问题标题】:Understanding the recursive CTE termination check了解递归 CTE 终止检查
【发布时间】:2018-02-03 22:39:44
【问题描述】:

在这个 sqlfiddle...

http://sqlfiddle.com/#!6/b6587/6

我收到以下错误....

语句终止。最大递归100已用完 在语句完成之前。

我了解在 CTE 的第二个选择的 where 子句中需要进行“终止检查”。即使您取消注释 WHERE 子句,我也会收到相同的错误。

我只是想了解 1) 为什么需要它...毕竟每个订单行都与每个客户行都有关系,并且 2) 因为需要“终止检查”,所以会是什么样子这个例子可以工作。

顺便说一句,我想看到这个查询的输出如下。

1,'George', 'Patton','',''
1,'','','<some date>', 'tank'
1,'','','<some date>', 'plane'
2,'Lewie', 'Puller','',''
2,'','','<some date>', 'Rifle'
2,'','','<some date>', 'Hand Grenade'

顺便说一句,请随意评论获得这种结果的其他方法(不使用递归 cte),但也不要使用游标或临时表。我想通过基于集合的操作来做到这一点。

编辑

作为记录,我知道加入会起作用,但我有理由询问递归。为了给您提供背景信息,我正在处理一个 EXPORT 文件的结果集。每行只有一列,称为 LINE。此外,导出文件需要按我的示例的顺序输出...详细信息需要在标题行下,并且该模式需要重复...标题/详细信息...标题/详细信息。我认为也许递归可以解决这个问题。但是我知道简单的连接也可以解决它,只要我可以将行排列成正确的顺序...标题/详细信息...标题/详细信息,也许可以通过某种方式对列中的输出进行排序正确的方法。

我的导出将不依赖于调用应用程序来格式化数据...存储过程需要格式化数据。

【问题讨论】:

  • 你为什么在这里使用递归? IT 将无休止地循环一遍又一遍地在订单表中查找相同的客户编号。似乎一个简单的连接会做任何你想做的事情,因为这些数据没有任何层次结构。查看this sqlfiddle 以使用“深度”跟踪递归步骤并将递归限制为 20。
  • 它超过了最大递归次数,默认情况下为 100。这是由于您遇到的查询问题 - 正如@JNevill 所说,这是一个无限递归
  • 有任何反馈吗?
  • @scsimon,感谢您的回答。我的问题缺乏背景。我已经编辑了答案以提供一些背景信息。我故意让我的 sqlfiddle 非常简单,以便可以解决递归问题。我真正的查询实际上确实有一些深度。

标签: sql sql-server sql-server-2008 recursion common-table-expression


【解决方案1】:

这是一个使用日期的更好示例。假设我们要建立一个日期表。 2017 年每个月都有 1 行。我们创建一个 @startDate 作为锚点,@endDate 作为终止符。我们将它们设置为相隔 12 个月,因为我们想要一年。然后,递归将通过DATEADD 函数将一个月添加到@startDate,直到在WHERE 子句中遇到终止符。我们知道需要 11 次递归才能达到 12 个月……即 11 个月 + 开始日期。如果我们将 MAXRECURSION 设置为小于 11 的任何值,那么它将失败,因为需要 11 来满足递归 CTE 中的 WHERE 子句,即终止符..

declare @startDate datetime = '20170101'
declare @endDate datetime = '20171201'

;WITH Months
as
(
    SELECT @startDate as TheDate       --anchor
    UNION ALL
    SELECT DATEADD(month, 1, TheDate)  --recursive
    FROM Months
    WHERE TheDate < @endDate           --terminator... i.e. continue until this condition is met

)


SELECT * FROM Months OPTION (MAXRECURSION 10) --change this to 11

对于您的查询,一个简单的连接就足够了。

select 
  firstName
  ,lastName
  ,orderDate
  ,productID
from
  customers c
inner join
  orders o on o.customerID = c.id

但是,我看到您试图以一种奇怪的格式返回它,应该在您使用的任何报告应用程序中进行处理。这将使您在没有递归的情况下关闭。

with cte as(
select 
  firstName
  ,lastName
  ,orderDate
  ,productID
  ,dense_rank() over(order by c.id) as RN
from
  customers c
inner join
  orders o on o.customerID = c.id)


select distinct
  firstName
  ,lastName
  ,null
  ,null
  ,RN
from 
  cte
union all
select
  ''
  ,''
  ,orderDate
  ,productID
  ,RN
from 
  cte
order by RN, firstName desc

【讨论】:

  • ,看看我的 sqlfiddle,你能让 sql fiddle 按我要求的顺序返回吗?您的工会将返回所有“标题”,然后返回所有“详细信息”。 ORDER 确实是这个问题中最重要的事情......标题/细节......标题/细节。我认为递归“可能”解决了这个问题。
  • 我很困惑@SethSpearman。我的 cte with union 会返回您的确切结果。
  • 对不起,我在找小提琴。上面 cmets 中的那个不是来自你的,我没有注意到……但你的代码确实有效。这是一个小提琴......sqlfiddle.com/#!6/b6587/36
  • 我必须检查一下 dense_rank() 函数。但是谢谢...很棒的解决方案。
猜你喜欢
  • 1970-01-01
  • 2014-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-25
  • 2021-09-29
  • 2015-08-21
相关资源
最近更新 更多