我认为以下三个SQL语句在语义上是相同的。数据库引擎将在内部将第二个和第三个查询扩展到第一个查询。
select ....
from T
where Id = 1
select *
from
(select .... from T) t
where Id = 1
select *
from
(select .... from T where Id = 1) t
但是,我发现窗口函数的行为有所不同。我有以下代码。
-- Prepare test data
with t1 as
(
select *
from (values ( 2, null), ( 3, 10), ( 5, -1), ( 7, null), ( 11, null), ( 13, -12), ( 17, null), ( 19, null), ( 23, 1759) ) v ( id, col1 )
)
select *
into #t
from t1
alter table #t add primary key (id)
go
以下查询返回所有行。
select
id, col1,
cast(substring(max(cast(id as binary(4)) + cast(col1 as binary(4)))
over (order by id
rows between unbounded preceding and 1 preceding), 5, 4) as int) as lastval
from
#t
id col1 lastval
-------------------
2 NULL NULL
3 10 NULL
5 -1 10
7 NULL -1
11 NULL -1
13 -12 -1
17 NULL -12
19 NULL -12
23 1759 -12
没有CTE /子查询:然后我添加了一个条件,只返回Id = 19
的行。
select
id, col1,
cast(substring(max(cast(id as binary(4)) + cast(col1 as binary(4))) over (order by id rows between unbounded preceding and 1 preceding), 5, 4) as int) as lastval
from
#t
where
id = 19;
但是,lastval
会返回null
?
使用CTE /子查询:现在条件应用于CTE:
with t as
(
select
id, col1,
cast(substring(max(cast(id as binary(4)) + cast(col1 as binary(4))) over (order by id rows between unbounded preceding and 1 preceding ), 5, 4) as int) as lastval
from
#t)
select *
from t
where id = 19;
-- Subquery
select
*
from
(select
id, col1,
cast(substring(max(cast(id as binary(4)) + cast(col1 as binary(4))) over (order by id rows between unbounded preceding and 1 preceding), 5, 4) as int) as lastval
from
#t) t
where
id = 19;
现在lastval
按预期返回-12
?
答案 0 :(得分:2)
导入SELECT语句的logic order of operations以了解第一个示例的结果。从Microsoft文档中,订单是从上到下:
- FROM
- ON
- JOIN
- WHERE
- GROUP BY
- WITH CUBE或WITH ROLLUP
- HAVING
- 选择
- DISTINCT
- ORDER BY
- TOP
醇>
请注意,WHERE
子句处理在SELECT
子句之前逻辑上发生。
正在过滤没有CTE的查询where id = 19
。操作顺序使where
在select
子句中的窗口函数之前处理。只有一行的id
为19.因此,where
在窗口函数处理rows between unbounded preceding and 1 preceding
之前将行限制为id = 19。由于窗口函数没有行,lastval
为null
。
将此与CTE进行比较。外部查询的过滤器尚未应用,因此CTE操作所有数据。 rows between unbounded preceding
找到之前的行。查询的外部部分将过滤器应用于中间结果,只返回已经具有正确lastval
的第19行。
您可以将CTE视为创建一个包含CTE数据的临时#Table。在将数据返回到外部查询之前,所有数据都在逻辑上处理为单独的表。示例中的CTE创建一个临时工作表,其中包含前一行中lastval
的所有行。然后,应用外部查询中的过滤器并将结果限制为id
19。
(实际上,CTE可以快捷方式并跳过生成数据,如果它可以提高性能而不影响结果.Izik Ben-Gan有一个great example的CTE,当它返回时会跳过处理足够的数据来满足查询。)
考虑如果将过滤器放在CTE中会发生什么。这应该与您提供的第一个示例查询完全相同。只有1行id = 19,因此窗口函数找不到任何前面的行:
with t as ( select id, col1,
cast(substring(max(cast(id as binary(4)) + cast(col1 as binary(4))) over ( order by id
rows between unbounded preceding and 1 preceding ), 5, 4) as int) as lastval
from #t
where id = 19 -- moved filter inside CTE
)
select *
from t
答案 1 :(得分:0)
窗口函数对结果集进行操作,因此当您添加where id = 19
时,结果集只有1行。由于您的窗口函数指定rows between unbounded preceding and 1 preceding
,因此没有前一行,并导致null
。
通过使用子查询/ cte,您允许窗口函数对未过滤的结果集(前面的行存在)进行操作,然后仅从该结果集where id = 19
中检索那些行。
答案 2 :(得分:0)
您要比较的查询不等同。
select id ,
(... ) as lastval
from #t
where id = 19;
只需要1行,所以' lastval'将从col1获取NULL,因为窗口函数找不到前一行。