递归查询错误
Recursive query error
在 SQL Server 2008+ 上,我正在尝试创建一个查询,它将成为视图的代码(因此,只允许一个语句......对吗?),即 returns一列所有可能的日期(实际上 datetime
但时间内容为空)介于 table dt
的第 insdate
列的最小值和最大值之间。
我试过这个:
;WITH daterange(mindate, maxdate) AS
(
SELECT
MIN(insdate) AS mindate,
MAX(insdate) AS maxdate
FROM dt
)
WITH t(specific_day) AS
(
SELECT mindate -- Seed Row
UNION ALL
SELECT specific_day + 1 -- Recursion
FROM t
WHERE specific_day + 1 <= maxdate
)
SELECT *
FROM t
OPTION (maxrecursion 5000)
但它失败了,出现了非常常见的错误消息 319。有趣的是,每个 'with' 子句在未组合时都按预期工作。我是不是漏掉了像 "recursive cte must be first" 这样的规则?我怎样才能达到我最初的目的?
改为:
;with daterange(mindate,maxdate) as
(
select min(insdate) as mindate,max(insdate) as maxdate from dt
)
, t(specific_day) AS (
SELECT mindate FROM daterange -- Seed Row
UNION ALL
SELECT specific_day+1 -- Recursion
FROM t
where specific_day+1<=(select maxdate from daterange)
)
select * from t
option (maxrecursion 5000)
如果要使用多个 CTE,则无需多次指定 WITH
,只需用逗号将它们链接起来即可:
WITH CTE1 AS (...),
CTE2 AS (...),
CTE3 AS ....
但我建议采用完全不同的方法。如果您有日历 table,那么这将成为一个非常基本的查询:
WITH DateRange (MinDate, MaxDate) AS
(
SELECT MIN(InsDate), MAX(InsDate)
FROM dt
)
SELECT c.Date
FROM dbo.Calendar AS c
INNER JOIN DateRange AS dr
ON dr.MinDate <= c.Date
AND dr.MaxDate >= c.Date;
如果您没有日历 table,那么您可以即时生成日期列表。更多关于此的阅读在这里:
- Generate a set or sequence without loops – part 1
- Generate a set or sequence without loops – part 2
- Generate a set or sequence without loops – part 3
第 3 部分特别相关,因为它涉及日期。
不过,最简单的方法是只生成一个范围大小的序列号列表,并将每个数字添加到您的开始日期,例如
WITH DateRange (MinDate, MaxDate) AS
(
SELECT MIN(InsDate), MAX(InsDate)
--FROM dt
FROM (VALUES (CONVERT(DATE, '20170101')), (CONVERT(DATE, '20180101'))) dt (InsDate)
), Numbers AS
( SELECT Number = ROW_NUMBER() OVER(ORDER BY n1.n) - 1
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS n1 (n)
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS n2 (n)
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS n3 (n)
)
SELECT DATEADD(DAY, n.Number, dr.MinDate)
FROM DateRange AS dr
INNER JOIN Numbers AS n
ON n.Number <= DATEDIFF(DAY, dr.MinDate, dr.MaxDate);
在 SQL Server 2008+ 上,我正在尝试创建一个查询,它将成为视图的代码(因此,只允许一个语句......对吗?),即 returns一列所有可能的日期(实际上 datetime
但时间内容为空)介于 table dt
的第 insdate
列的最小值和最大值之间。
我试过这个:
;WITH daterange(mindate, maxdate) AS
(
SELECT
MIN(insdate) AS mindate,
MAX(insdate) AS maxdate
FROM dt
)
WITH t(specific_day) AS
(
SELECT mindate -- Seed Row
UNION ALL
SELECT specific_day + 1 -- Recursion
FROM t
WHERE specific_day + 1 <= maxdate
)
SELECT *
FROM t
OPTION (maxrecursion 5000)
但它失败了,出现了非常常见的错误消息 319。有趣的是,每个 'with' 子句在未组合时都按预期工作。我是不是漏掉了像 "recursive cte must be first" 这样的规则?我怎样才能达到我最初的目的?
改为:
;with daterange(mindate,maxdate) as
(
select min(insdate) as mindate,max(insdate) as maxdate from dt
)
, t(specific_day) AS (
SELECT mindate FROM daterange -- Seed Row
UNION ALL
SELECT specific_day+1 -- Recursion
FROM t
where specific_day+1<=(select maxdate from daterange)
)
select * from t
option (maxrecursion 5000)
如果要使用多个 CTE,则无需多次指定 WITH
,只需用逗号将它们链接起来即可:
WITH CTE1 AS (...),
CTE2 AS (...),
CTE3 AS ....
但我建议采用完全不同的方法。如果您有日历 table,那么这将成为一个非常基本的查询:
WITH DateRange (MinDate, MaxDate) AS
(
SELECT MIN(InsDate), MAX(InsDate)
FROM dt
)
SELECT c.Date
FROM dbo.Calendar AS c
INNER JOIN DateRange AS dr
ON dr.MinDate <= c.Date
AND dr.MaxDate >= c.Date;
如果您没有日历 table,那么您可以即时生成日期列表。更多关于此的阅读在这里:
- Generate a set or sequence without loops – part 1
- Generate a set or sequence without loops – part 2
- Generate a set or sequence without loops – part 3
第 3 部分特别相关,因为它涉及日期。
不过,最简单的方法是只生成一个范围大小的序列号列表,并将每个数字添加到您的开始日期,例如
WITH DateRange (MinDate, MaxDate) AS
(
SELECT MIN(InsDate), MAX(InsDate)
--FROM dt
FROM (VALUES (CONVERT(DATE, '20170101')), (CONVERT(DATE, '20180101'))) dt (InsDate)
), Numbers AS
( SELECT Number = ROW_NUMBER() OVER(ORDER BY n1.n) - 1
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS n1 (n)
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS n2 (n)
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS n3 (n)
)
SELECT DATEADD(DAY, n.Number, dr.MinDate)
FROM DateRange AS dr
INNER JOIN Numbers AS n
ON n.Number <= DATEDIFF(DAY, dr.MinDate, dr.MaxDate);