标题说明了这一点,为什么我不能在SQL Server的where子句中使用窗口函数?
这个查询很有意义:
select id, sales_person_id, product_type, product_id, sale_amount
from Sales_Log
where 1 = row_number() over(partition by sales_person_id, product_type, product_id order by sale_amount desc)
但它不起作用。有没有比CTE /子查询更好的方法?
修改
这值得用CTE查询:
with Best_Sales as (
select id, sales_person_id, product_type, product_id, sale_amount, row_number() over (partition by sales_person_id, product_type, product_id order by sales_amount desc) rank
from Sales_log
)
select id, sales_person_id, product_type, product_id, sale_amount
from Best_Sales
where rank = 1
修改
对于使用子查询显示的答案,+1,但实际上我正在寻找无法在where子句中使用窗口函数的原因。
答案 0 :(得分:55)
为什么我不能在SQL Server的where子句中使用窗口函数?
一个答案,虽然不是特别有用,但是因为规范说你做不到。
参见Itzik Ben Gan的文章 - Logical Query Processing: What It Is And What It Means to You,特别是the image here。在处理了所有SELECT
/ WHERE
/ JOIN
/ GROUP BY
条款后,在结果集HAVING
上评估窗口函数(步骤5.1)。
我真的在寻找无法使用的原因 窗口函数在where子句中。
WHERE
子句中不允许它们的原因是它会产生歧义。从High-Performance T-SQL Using Window Functions(第25页)中偷取Itzik Ben Gan的例子
假设你的桌子是
CREATE TABLE T1
(
col1 CHAR(1) PRIMARY KEY
)
INSERT INTO T1 VALUES('A'),('B'),('C'),('D'),('E'),('F')
您的查询
SELECT col1
FROM T1
WHERE ROW_NUMBER() OVER (ORDER BY col1) <= 3
AND col1 > 'B'
什么是正确的结果?您是否期望在行编号之前或之后运行col1 > 'B'
谓词?
答案 1 :(得分:9)
不需要CTE,只需在子查询中使用窗口函数:
select id, sales_person_id, product_type, product_id, sale_amount
from
(
select id, sales_person_id, product_type, product_id, sale_amount,
row_number() over(partition by sales_person_id, product_type, product_id order by sale_amount desc) rn
from Sales_Log
) sl
where rn = 1
编辑,将我的评论移至答案。
在实际选择WHERE
子句之后的数据之前,不会执行窗口函数。因此,如果您尝试在row_number
子句中使用WHERE
,则尚未分配该值。
答案 2 :(得分:7)
首先,它称为 all-at-once operation
&#34; All-at-Once Operations&#34;意味着所有表达式都相同 逻辑查询过程阶段在逻辑上同时进行评估。
很棒的章节对窗口函数的影响:
假设你有:
CREATE TABLE #Test ( Id INT) ;
INSERT INTO #Test VALUES ( 1001 ), ( 1002 ) ;
SELECT Id
FROM #Test
WHERE Id = 1002
AND ROW_NUMBER() OVER(ORDER BY Id) = 1;
All-at-Once操作告诉我们在同一时间点逻辑评估这两个条件。因此,SQL Server可以 基于的任意顺序评估WHERE子句中的条件 估计执行计划。所以这里的主要问题是哪个条件 首先评估。
案例1:
If ( Id = 1002 ) is first, then if ( ROW_NUMBER() OVER(ORDER BY Id) = 1 )
结果:1002
案例2:
If ( ROW_NUMBER() OVER(ORDER BY Id) = 1 ), then check if ( Id = 1002 )
结果:空
所以我们有一个悖论。
此示例说明了为什么我们不能在WHERE子句中使用Window Functions。 您可以更多地考虑这个并找到Window Functions的原因 允许仅在 SELECT 和 ORDER BY 子句中使用!
<强>附录强>
Terradata支持QUALIFY
子句:
根据用户指定的搜索条件过滤先前计算的有序分析函数的结果。
SELECT Id
FROM #Test
WHERE Id = 1002
QUALIFY ROW_NUMBER() OVER(ORDER BY Id) = 1;
答案 3 :(得分:3)
您不一定需要使用CTE,使用row_number()后可以查询结果集
select row, id, sales_person_id, product_type, product_id, sale_amount
from (
select
row_number() over(partition by sales_person_id,
product_type, product_id order by sale_amount desc) AS row,
id, sales_person_id, product_type, product_id, sale_amount
from Sales_Log
) a
where row = 1
答案 4 :(得分:1)
不幸的是,当你执行窗口函数时,即使你的where谓词是合法的,SQL也会生你的气。您可以在select语句中创建具有该值的cte或嵌套选择,然后稍后使用该值引用CTE或嵌套选择。简单的例子应该是自我解释的。如果你真的讨厌cte用于执行大型数据集的某些性能问题,你可以随时删除临时表或表变量。
declare @Person table ( PersonID int identity, PersonName varchar(8));
insert into @Person values ('Brett'),('John');
declare @Orders table ( OrderID int identity, PersonID int, OrderName varchar(8));
insert into @Orders values (1, 'Hat'),(1,'Shirt'),(1, 'Shoes'),(2,'Shirt'),(2, 'Shoes');
--Select
-- p.PersonName
--, o.OrderName
--, row_number() over(partition by o.PersonID order by o.OrderID)
--from @Person p
-- join @Orders o on p.PersonID = o.PersonID
--where row_number() over(partition by o.PersonID order by o.orderID) = 2
-- yields:
--Msg 4108, Level 15, State 1, Line 15
--Windowed functions can only appear in the SELECT or ORDER BY clauses.
;
with a as
(
Select
p.PersonName
, o.OrderName
, row_number() over(partition by o.PersonID order by o.OrderID) as rnk
from @Person p
join @Orders o on p.PersonID = o.PersonID
)
select *
from a
where rnk >= 2 -- only orders after the first one.
答案 5 :(得分:1)
最后,有一种老式的,前SQL Server 2005的方式,带有相关的子查询:
select *
from Sales_Log sl
where sl.id = (
Select Top 1 id
from Sales_Log sl2
where sales_person_id = sl.sales_person_id
and product_type = sl.product_type
and product_id = sl.product_id
order by sale_amount desc
)
我只是为了完整而给你这个。
答案 6 :(得分:1)
这是一个旧帖子,但我会尝试专门回答该主题中表达的问题。
为什么在where子句中没有窗口函数?
SELECT
语句具有键入顺序中指定的以下主要子句:
SELECT DISTINCT TOP list
FROM JOIN ON / APPLY / PIVOT / UNPIVOT
WHERE
GROUP BY WITH CUBE / WITH ROLLUP
HAVING
ORDER BY
OFFSET-FETCH
逻辑查询处理订单或绑定订单是概念解释订单,它定义了查询的正确性。此顺序确定一个步骤中定义的对象何时可用于后续步骤中的子句。
----- Relational result
1. FROM
1.1. ON JOIN / APPLY / PIVOT / UNPIVOT
2. WHERE
3. GROUP BY
3.1. WITH CUBE / WITH ROLLUP
4. HAVING
---- After the HAVING step the Underlying Query Result is ready
5. SELECT
5.1. SELECT list
5.2. DISTINCT
----- Relational result
----- Non-relational result (a cursor)
6. ORDER BY
7. TOP / OFFSET-FETCH
----- Non-relational result (a cursor)
例如,如果查询处理器可以绑定(访问)FROM
子句中定义的表或视图,则这些对象及其列可供所有后续步骤使用。
相反,SELECT
子句之前的所有子句都不能引用SELECT
子句中定义的任何列别名或派生列。但是,这些列可以由后续子句引用,例如ORDER BY
子句。
OVER
子句确定在应用关联窗口函数之前对行集的分区和排序。也就是说,OVER
子句在 底层查询结果 集中定义一个窗口或用户指定的行集,窗口函数计算该窗口的结果。< / p>
Msg 4108, Level 15, State 1, …
Windowed functions can only appear in the SELECT or ORDER BY clauses.
背后的原因是因为逻辑查询处理在T-SQL
中的工作方式。由于基础查询结果仅在逻辑查询处理到达SELECT
步骤5.1时建立。 (即,在处理FROM
,WHERE
,GROUP BY
和HAVING
步骤之后),仅在SELECT
和{{1}中允许窗口函数查询的子句。
需要注意的是,窗口函数仍然是关系层的一部分,甚至关系模型也不处理有序数据。 ORDER BY
步骤5.1之后的结果。任何窗口功能仍然是关系型的。
另外,严格地说,SELECT
子句中不允许使用窗口函数的原因不是因为它会产生歧义,而是因为逻辑查询处理处理的顺序{{ 1}} {}}}中的陈述。
答案 7 :(得分:1)
基本上,第一个“ WHERE”子句条件由sql读取,并且向表中查找了相同的列/值id,但表中的row_num = 1仍然不存在。因此,它将不起作用。 因此,我们将首先使用括号,然后再编写WHERE子句。