ОПЦИЯ (MAXRECURSION X) в операторе MERGE с CTE не работает? - PullRequest
0 голосов
/ 21 декабря 2018

У меня есть следующий 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.

Ответы [ 2 ]

0 голосов
/ 28 декабря 2018

Советы по запросу всегда идут в самом конце запроса.Так что просто переместите 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)
    ;
0 голосов
/ 28 декабря 2018

Вы можете использовать табличную переменную со структурой вашего CTE для хранения результатов, которые будут использоваться позже в операторе merge:

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

Затем вы можете выполнить свой CTE с записью MAXRECURSION 0результаты в табличной переменной:

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);
...