带有 CTE 的 MERGE 语句中的 OPTION (MAXRECURSION X) 不起作用?
OPTION (MAXRECURSION X) in MERGE Statement with CTE not working?
我有以下 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)
这将return以下数据(从 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
...
现在我想将数据保存在特定的 table 中,其中可能已经有一些数据(甚至更糟:可能有一些额外的字段)。所以我的想法是使用这样的合并语句:
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);
但这总是失败并出现以下错误:
Incorrect syntax near the keyword 'OPTION'.
遗漏 OPTION (MAXRECURSION 0)
不是一个选项,因为 CTE 进行了 100 多次递归,然后 SQL 只是响应:
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
我必须在哪里设置OPTION (MAXRECURSION 0)
?这有可能吗?遗憾的是,我没有在 Microsoft 文档中找到任何关于此 "special" 案例的提及。
您可以使用具有 CTE 结构的 table 变量来存储稍后将在 merge
语句中使用的结果:
declare @tmp table (
[Datum] date,
[Year] int,
[Month] int,
[IsoWeek] int,
[YearMonth] int,
[SelID] int,
[WeekYear] int
)
然后您可以通过 MAXRECURSION 0
将结果写入 table 变量来执行 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)
现在您应该可以将 table SYS_LIST_DATEHLP
与 table 变量合并 @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);
查询提示总是出现在查询的最后。所以只需将 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)
;
我有以下 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)
这将return以下数据(从 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
...
现在我想将数据保存在特定的 table 中,其中可能已经有一些数据(甚至更糟:可能有一些额外的字段)。所以我的想法是使用这样的合并语句:
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);
但这总是失败并出现以下错误:
Incorrect syntax near the keyword 'OPTION'.
遗漏 OPTION (MAXRECURSION 0)
不是一个选项,因为 CTE 进行了 100 多次递归,然后 SQL 只是响应:
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
我必须在哪里设置OPTION (MAXRECURSION 0)
?这有可能吗?遗憾的是,我没有在 Microsoft 文档中找到任何关于此 "special" 案例的提及。
您可以使用具有 CTE 结构的 table 变量来存储稍后将在 merge
语句中使用的结果:
declare @tmp table (
[Datum] date,
[Year] int,
[Month] int,
[IsoWeek] int,
[YearMonth] int,
[SelID] int,
[WeekYear] int
)
然后您可以通过 MAXRECURSION 0
将结果写入 table 变量来执行 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)
现在您应该可以将 table SYS_LIST_DATEHLP
与 table 变量合并 @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);
查询提示总是出现在查询的最后。所以只需将 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)
;