【问题标题】:TSQL CTE Maxrecursion over workaround?TSQL CTE Maxrecursion 解决方法?
【发布时间】:2015-07-24 00:28:53
【问题描述】:

我有一个超过 400 万条记录的表。我想按特定列分解记录,但是我达到了 32767 的 maxrecusion 限制。有没有办法在不使用 CTE 的情况下分解记录。我尝试了临时表和 while 循环方法,但是我的新数据集应该在视图中调用,所以 #temp 表和表变量对我没有任何帮助。

基表:

select 
     ID,
     CAST(start_time AS datetime) AS start_time,
     DATEADD(ss,duration,CAST(start_time AS datetime))  AS end_time
     ,duration
FROM 
    dbo.RunTable  
where id =1

输出:

ID  start_time  end_time        duration
1   2009-12-10  13:50:34.000    2009-12-10 18:21:09.000 16235

使用 CTE 查询:

WITH  td_run_base (ID,start_time,end_time1,end_time) AS
(SELECT  ID,
     CAST(start_time AS datetime) AS start_time,
     DATEADD(ss, 60*60, CAST(start_time AS datetime)) AS end_time1,
     DATEADD(ss,duration,CAST(start_time AS datetime))  AS end_time
FROM 
    dbo.RunTable 
where Id = 1
UNION ALL
SELECT 
        ID,
        DATEADD(ss, 0, end_time1) as start_time,
        DATEADD(ss, 60*60,end_time1) AS end_time1,
        end_time
FROM td_run_base WHERE CAST(end_time1 AS datetime) < CAST(end_time
AS   datetime)
),
td_run_cte AS
(SELECT ID,start_time,
CASE WHEN end_time1 > end_time THEN end_time ELSE end_time1 END AS end_time
FROM td_run_base)
SELECT td_run_cte.ID,
   td_run_cte.start_time,
   td_run_cte.end_time,
   datediff(ss,start_time,end_time) as Duration
 FROM 
   td_run_cte
 OPTION (MAXRECURSION 32767) -- maxrecursion for sql server standard 
 go

输出:

ID     start_time                end_time          Duration
1   2009-12-10 13:50:34.000 2009-12-10 14:50:34.000 3600
1   2009-12-10 14:50:34.000 2009-12-10 15:50:34.000 3600
1   2009-12-10 15:50:34.000 2009-12-10 16:50:34.000 3600
1   2009-12-10 16:50:34.000 2009-12-10 17:50:34.000 3600
1   2009-12-10 17:50:34.000 2009-12-10 18:21:09.000 1835

我最初的想法是使用此查询创建一个视图,但是我在该表中有超过 400 万条记录,我当然不能在视图中使用#temp 表。有没有办法解决它。我使用临时表和 while 循环创建一个静态表,并从视图中调用该表。但是,由于获得了我的许可,我不应该采用这种方法。它只能是查询或视图。

【问题讨论】:

  • 您能否阐明按特定列细分记录的意思?也许样本数据和输出会有所帮助,而您现在使用的查询也会有所帮助。
  • OPTION (MAXRECURSION 0)。至于您关于更改查询、示例数据、所需结果和当前查询的问题,都可以帮助其他人了解您要做什么。
  • 递归最大值的存在是有原因的,通常是为了停止无限循环。如果您的数据具有固有的层次结构,您真的期望该层次结构中需要 32767 个级别吗?重新考虑您正在尝试的内容,如前所述:请提供示例数据 + 预期结果 + 现有查询
  • 您始终可以使用数字表来避免 CTE
  • 数字表?你的意思是临时表或表变量?

标签: sql sql-server tsql common-table-expression


【解决方案1】:

您可以通过使用数字表(只是一个包含 1-n 值的表)来避免递归。 Table1 在我的查询中表示您的第一个查询的输出,因此您必须使用子查询/cte,同样,如果您无法构建数字表,您也必须使用 cte/子查询:

    SELECT a.ID
      ,DATEADD(hour,n-1,start_time) AS Start_Time
      ,CASE WHEN DATEADD(hour,n,start_time) <= End_Time THEN DATEADD(hour,n,start_time)
            ELSE End_Time      
       END as End_Time
      ,DATEDIFF(ss,DATEADD(hour,n-1,start_time),CASE WHEN DATEADD(hour,n,start_time) <= End_Time THEN DATEADD(hour,n,start_time)
            ELSE End_Time      
       END) AS duration
FROM Table1 a
JOIN lkp_Numbers b
  ON b.N BETWEEN 1 AND duration/3600 + 1

创建数字表的最快方法之一:

;with a AS (SELECT 1 n UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
              SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1)
SELECT TOP 1000000 IDENTITY(int,1,1) AS N
INTO lkp_Numbers
FROM a a, a b, a c, a d, a e, a f 

如果必须使用 cte,则不能使用 IDENTITY,但可以使用 ROW_NUMBER() 或其他方法。不过,数字表会派上用场。

演示:SQL Fiddle

【讨论】:

  • 感谢@Hart CO 身份列在我不得不做的情况下无法使用
【解决方案2】:

感谢@Hart CO 身份列在我必须做的视图中不起作用

row_number() over ( order by a.n) as N 

我也得到了子查询的工作

SELECT a.ID
  ,DATEADD(hour,n-1,start_time) AS Start_Time
  ,CASE WHEN DATEADD(hour,n,start_time) <=  DATEADD(ss,duration,CAST(start_time AS datetime)) THEN DATEADD(hour,n,start_time)
        ELSE  DATEADD(ss,duration,CAST(start_time AS datetime))     
   END as End_Time
  ,DATEDIFF(ss,DATEADD(hour,n-1,start_time),CASE WHEN     DATEADD(hour,n,start_time) <=  DATEADD(ss,duration,CAST(start_time AS datetime)) THEN DATEADD(hour,n,start_time)
        ELSE  DATEADD(ss,duration,CAST(start_time AS datetime))    
   END) AS duration
FROM table1 a 
JOIN (select TOP 1000000 row_number() over ( order by t1.number) as N
from   master..spt_values t1 
   cross join master..spt_values t2) b
 ON b.N BETWEEN 1 AND duration/3600 + 1 

再次感谢您的帮助。

【讨论】:

    猜你喜欢
    • 2012-11-03
    • 1970-01-01
    • 2013-02-11
    • 1970-01-01
    • 2010-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-17
    相关资源
    最近更新 更多