递归查询错误

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,那么您可以即时生成日期列表。更多关于此的阅读在这里:

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