CTE无法在MERGE语句中使用OPTION(MAXRECURSION X)吗?

时间:2018-12-21 13:57:53

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

我具有以下按预期工作的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中没有发现任何有关此“特殊”案例的提示。

2 个答案:

答案 0 :(得分:1)

查询提示总是出现在查询的最后。因此,只需将Md=[0.000000 0.0000 340.216815 0.000000 1.5625 570.718050 0.000000 4.6875 769.256473 6.500000 0.0000 331.115127 6.500000 1.5370 510.419428 6.500000 4.6108 719.346166 13.000000 0.0000 325.858265 13.000000 1.5114 426.599681 13.000000 4.5341 671.896040 19.500000 0.0000 330.567837 19.500000 1.4702 383.856624 19.500000 4.4105 643.279493 26.000000 0.0000 333.606362 26.000000 1.4602 381.784469 26.000000 4.3807 648.680568]; 移至最后一个半冒号之前即可。

// start the session 
session_start();

$bShowBanner = true;    

if(isset($_SESSION['BannerShown'])){
    $bShowBanner = false;
}else{
    $_SESSION['BannerShown'] = true;
}
?>

<div class="homeslidermain" style="display:<?php echo ($bShowBanner ? 'block' : 'none'); ?>"> 
    <?php putRevSlider("typewriter-effect", "homepage") ?>
</div>

答案 1 :(得分:0)

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

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

然后您可以使用MAXRECURSION 0将CTE写入表变量中来执行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);