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
.
来自 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
.