【问题标题】:OPTION (MAXRECURSION X) in MERGE Statement with CTE not working?使用 CTE 的 MERGE 语句中的 OPTION (MAXRECURSION X) 不起作用?
【发布时间】:2018-12-21 13:57:52
【问题描述】:

我有以下按预期工作的 CTE (Microsoft SQL Server 2017):

WITH DATE_CTE (Datum)
as
(
    SELECT DATEFROMPARTS(1970, 1, 1)
    UNION ALL
    SELECT DATEADD(Day, 1, DATE_CTE.Datum) FROM DATE_CTE WHERE YEAR(DATE_CTE.Datum) < 2100 
) 
SELECT DATE_CTE.Datum, 
        YEAR(DATE_CTE.Datum) as [Year], 
        MONTH(DATE_CTE.Datum) as [Month], 
        DATEPART(ISO_WEEK, DATE_CTE.Datum) as IsoWeek, 
        CAST(YEAR(DATE_CTE.Datum) AS VARCHAR(4)) + IIF(MONTH(DATE_CTE.Datum) < 10, '0' + CAST(MONTH(DATE_CTE.Datum) AS VARCHAR(1)), CAST(MONTH(DATE_CTE.Datum) AS VARCHAR(2))) as YearMonth, 
        -1 as SelID,  
        CASE WHEN MONTH(DATE_CTE.Datum) = 1 AND DATEPART(ISO_WEEK, DATE_CTE.Datum) >= 52 THEN YEAR(DATE_CTE.Datum)-1 WHEN MONTH(DATE_CTE.Datum) = 12 AND DATEPART(ISO_WEEK, DATE_CTE.Datum) = 1 THEN YEAR(DATE_CTE.Datum)+1 ELSE YEAR(DATE_CTE.Datum) END as WeekYear
FROM DATE_CTE OPTION (MAXRECURSION 0)

这将返回以下数据(从 1970 到 2100 -> 47483 数据行):

    Date       Year        Month       IsoWeek     YearMonth SelID       WeekYear
    ---------- ----------- ----------- ----------- --------- ----------- -----------
    1970-01-01 1970        1           1           197001    -1          1970
    1970-01-02 1970        1           1           197001    -1          1970
    1970-01-03 1970        1           1           197001    -1          1970
    1970-01-04 1970        1           1           197001    -1          1970
    1970-01-05 1970        1           2           197001    -1          1970
    1970-01-06 1970        1           2           197001    -1          1970
    1970-01-07 1970        1           2           197001    -1          1970
    1970-01-08 1970        1           2           197001    -1          1970
    1970-01-09 1970        1           2           197001    -1          1970
    1970-01-10 1970        1           2           197001    -1          1970
    ... 

现在我想将数据保存在一个特定的表中,该表中可能已经有一些数据(更糟糕的是:可能有一些额外的字段)。所以我的想法是使用这样的合并语句:

WITH DATE_CTE (Datum)
as
(
    SELECT DATEFROMPARTS(1970, 1, 1)
    UNION ALL
    SELECT DATEADD(Day, 1, DATE_CTE.Datum) FROM DATE_CTE WHERE YEAR(DATE_CTE.Datum) < 2100 
) 
MERGE INTO SYS_LIST_DATEHLP AS Target  
USING (
        SELECT 
                DATE_CTE.Datum, 
                YEAR(DATE_CTE.Datum), 
                MONTH(DATE_CTE.Datum), 
                DATEPART(ISO_WEEK, DATE_CTE.Datum), 
                CAST(YEAR(DATE_CTE.Datum) AS VARCHAR(4)) + IIF(MONTH(DATE_CTE.Datum) < 10, '0' + CAST(MONTH(DATE_CTE.Datum) AS VARCHAR(1)), 
                CAST(MONTH(DATE_CTE.Datum) AS VARCHAR(2))), 
                -1, 
                CASE WHEN MONTH(DATE_CTE.Datum) = 1 AND DATEPART(ISO_WEEK, DATE_CTE.Datum) >= 52 THEN YEAR(DATE_CTE.Datum)-1 WHEN MONTH(DATE_CTE.Datum) = 12 AND DATEPART(ISO_WEEK, DATE_CTE.Datum) = 1 THEN YEAR(DATE_CTE.Datum)+1 ELSE YEAR(DATE_CTE.Datum) END
        FROM DATE_CTE OPTION (MAXRECURSION 0)
       )  
       AS Source (SYS_DATE, SYS_YEAR, SYS_MONTH, [WEEK], KAPMONAT, SEL_ID, WEEKYEAR)  
    ON Target.SYS_DATE = Source.SYS_DATE  
WHEN MATCHED THEN  
    UPDATE SET SYS_YEAR = Source.SYS_YEAR, SYS_MONTH = Source.SYS_MONTH, [WEEK] = Source.[WEEK], KAPMONAT = Source.KAPMONAT, SEL_ID = Source.SEL_ID, WEEKYEAR = Source.WEEKYEAR
WHEN NOT MATCHED BY TARGET THEN  
    INSERT (SYS_DATE, SYS_YEAR, SYS_MONTH, [WEEK], KAPMONAT, SEL_ID, WEEKYEAR)
    VALUES (Source.SYS_DATE, Source.SYS_YEAR, Source.SYS_MONTH, Source.[WEEK], Source.KAPMONAT, Source.SEL_ID, Source.WEEKYEAR);

但这总是失败并出现以下错误:

关键字“OPTION”附近的语法不正确。

省略OPTION (MAXRECURSION 0) 不是一种选择,因为 CTE 进行了超过 100 次递归,然后 SQL 仅响应:

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

我必须在哪里设置OPTION (MAXRECURSION 0)?这甚至可能吗?遗憾的是,我在 Microsoft Docs 中没有找到任何提及此“特殊”案例的内容。

【问题讨论】:

    标签: sql sql-server tsql sql-server-2017


    【解决方案1】:

    查询提示总是出现在查询的最后。所以只需将OPTION (MAXRECURSION 0) 移到最后一个分号之前。

    /* Rest of query omitted*/
    WHEN NOT MATCHED BY TARGET THEN  
        INSERT (SYS_DATE, SYS_YEAR, SYS_MONTH, [WEEK], KAPMONAT, SEL_ID, WEEKYEAR)
        VALUES (Source.SYS_DATE, Source.SYS_YEAR, Source.SYS_MONTH, Source.[WEEK], Source.KAPMONAT, Source.SEL_ID, Source.WEEKYEAR)
         OPTION (MAXRECURSION 0)
        ;
    

    【讨论】:

    • 啊,谢谢!我认为这可能与 OPTION() 的放置有关,但我没有找到任何东西。
    【解决方案2】:

    您可以使用具有 CTE 结构的表变量来存储稍后将在 merge 语句中使用的结果:

    declare @tmp table (
                            [Datum]     date, 
                            [Year]      int, 
                            [Month]     int,
                            [IsoWeek]   int, 
                            [YearMonth] int, 
                            [SelID]     int, 
                            [WeekYear]  int
                        )
    

    然后您可以使用MAXRECURSION 0 执行您的 CTE,将结果写入表变量中:

    WITH DATE_CTE (Datum)
    as
    (
        SELECT DATEFROMPARTS(1970, 1, 1)
        UNION ALL
        SELECT DATEADD(Day, 1, DATE_CTE.Datum) FROM DATE_CTE WHERE YEAR(DATE_CTE.Datum) < 2100 
    ) 
    insert into @tmp
    SELECT DATE_CTE.Datum, 
            YEAR(DATE_CTE.Datum) as [Year], 
            MONTH(DATE_CTE.Datum) as [Month], 
            DATEPART(ISO_WEEK, DATE_CTE.Datum) as IsoWeek, 
            CAST(YEAR(DATE_CTE.Datum) AS VARCHAR(4)) + IIF(MONTH(DATE_CTE.Datum) < 10, '0' + CAST(MONTH(DATE_CTE.Datum) AS VARCHAR(1)), CAST(MONTH(DATE_CTE.Datum) AS VARCHAR(2))) as YearMonth, 
            -1 as SelID,  
            CASE WHEN MONTH(DATE_CTE.Datum) = 1 AND DATEPART(ISO_WEEK, DATE_CTE.Datum) >= 52 THEN YEAR(DATE_CTE.Datum)-1 WHEN MONTH(DATE_CTE.Datum) = 12 AND DATEPART(ISO_WEEK, DATE_CTE.Datum) = 1 THEN YEAR(DATE_CTE.Datum)+1 ELSE YEAR(DATE_CTE.Datum) END as WeekYear
    FROM DATE_CTE OPTION (MAXRECURSION 0)
    

    现在您应该可以将表 SYS_LIST_DATEHLP 与表变量 @tmp 合并:

    MERGE INTO SYS_LIST_DATEHLP AS Target  
    USING (
            SELECT 
                 [Datum]     
                ,[Year]     
                ,[Month]    
                ,[IsoWeek]  
                ,[YearMonth]
                ,[SelID]    
                ,[WeekYear] 
            FROM @tmp  
           )  
           AS Source (SYS_DATE, SYS_YEAR, SYS_MONTH, [WEEK], KAPMONAT, SEL_ID, WEEKYEAR)  
        ON Target.SYS_DATE = Source.SYS_DATE  
    WHEN MATCHED THEN  
        UPDATE SET SYS_YEAR = Source.SYS_YEAR, SYS_MONTH = Source.SYS_MONTH, [WEEK] = Source.[WEEK], KAPMONAT = Source.KAPMONAT, SEL_ID = Source.SEL_ID, WEEKYEAR = Source.WEEKYEAR
    WHEN NOT MATCHED BY TARGET THEN  
        INSERT (SYS_DATE, SYS_YEAR, SYS_MONTH, [WEEK], KAPMONAT, SEL_ID, WEEKYEAR)
        VALUES (Source.SYS_DATE, Source.SYS_YEAR, Source.SYS_MONTH, Source.[WEEK], Source.KAPMONAT, Source.SEL_ID, Source.WEEKYEAR);
    

    【讨论】:

      猜你喜欢
      • 2020-12-29
      • 2021-02-14
      • 2016-05-15
      • 2012-11-03
      • 2014-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多