T-SQL / SQL 服务器中真正嵌套的 CTE

Truly nested CTEs in T-SQL / SQL Server

来自 Postgres 我习惯这样做:

with a as (
 with b as (
  SELECT
  ...
 )
 SELECT
 ...
),
c as (
 SELECT
 ...
)
SELECT
...

但是,a 和 b 的这种嵌套(或 CTE 层次结构)不会在 T-SQL 中执行。

是否有解决此问题的方法,如 Can you create nested WITH clauses for Common Table Expressions? 中建议的简单重写,如

with a as (
 SELECT
 ...
 ),
b as (
 SELECT
 ...
),
c as (
 SELECT
 ...
)
SELECT
...

在我的情况下是不可能的(见下面的查询),在许多其他情况下也是如此。

很高兴得到一些指点,因为我是 T-SQL 的新手。


--generate a time-series in 15min intervals, ending with the last incoming user
WITH last_timeslot AS (
--get the last ts from one of the two tables with less recent entries
    SELECT
        dateadd(
            MINUTE,
                datediff(
                    MINUTE,
                    0,
                    LEAST(
                        (SELECT
                            MAX(ts)
                        FROM dbo.user u )
                        ,
                        (SELECT
                            MAX(ts)
                        FROM dbo.user_history uh )
                    )
                )/ 15 * 15, --round to 15min slot
            0
        ) AS last_timeslot_ts
),
timeslot_series AS (
    --recursively generate a timeseries going back 14 days from the last timeslot, in 15min steps
    WITH cte AS (
      SELECT DATEADD(DAY,-14,(SELECT last_timeslot_ts FROM last_timeslot)) AS slot
      UNION ALL
      SELECT  DATEADD(MINUTE,15,slot) 
      FROM    cte   
      WHERE   DATEADD(MINUTE,15,slot) < (SELECT last_timeslot_ts FROM last_timeslot)
    )
    SELECT  slot
    FROM    cte
    OPTION (MAXRECURSION 0)
),
c as (
--other stuff happens
SELECT
...
FROM timeslot_series
LEFT JOIN
...
)
SELECT ...

我将简化为两层嵌套,因为这足以演示解决方案。我们从这个开始:

with a as (
 with b as (
  SELECT
  ...
 )
 SELECT
 ...
),
SELECT
...

据推测,a 需要引用 b 作为其查询的一部分。因此,当我们尝试取消嵌套它们并改为按如下所示顺序列出它们时,它还不起作用:

with a as (
 SELECT
 ...
 ),
b as (
 SELECT
 ...
),
SELECT
...

要解决此问题,我们需要反转我们定义每个 CTE 的顺序。即首先将查询的最内层定义为CTE,然后再向外工作:

with b as (
 SELECT
 ...
 ),
a as (
 SELECT
 ...
),
SELECT
...

现在您将能够在 a 中引用 b(如果需要,在最后的 SELECT 中再次引用)。如果我们回到原来的例子并再次包含 c,它现在会排在第一位,然后是 b,然后是 a.