为什么在where子句中没有窗口函数?

Cri*_*ole 47 sql t-sql sql-server window-functions

标题说明了这一点,为什么我不能在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)
Run Code Online (Sandbox Code Playgroud)

但它不起作用.有没有比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
Run Code Online (Sandbox Code Playgroud)

编辑

用子查询显示答案的+1,但实际上我正在寻找无法在where子句中使用窗口函数的原因.

Mar*_*ith 60

为什么我不能在SQL Server的where子句中使用窗口函数?

一个答案,虽然不是特别有用,但是因为规范说你做不到.

请参阅Itzik Ben Gan的文章 - 逻辑查询处理:它是什么以及它对您意味着什么,特别是这里的图像.在处理完SELECT所有WHERE/ JOIN/ GROUP BY/ HAVING子句之后,在结果集开始时评估窗口函数(步骤5.1).

我真的在寻找无法在where子句中使用窗口函数的原因.

它们在WHERE条款中不被允许的原因是它会产生歧义.使用窗口函数高性能T-SQL中窃取Itzik Ben Gan的示例(第25页)

假设你的桌子是

CREATE TABLE T1
(
col1 CHAR(1) PRIMARY KEY
)

INSERT INTO T1 VALUES('A'),('B'),('C'),('D'),('E'),('F')
Run Code Online (Sandbox Code Playgroud)

和你的查询

SELECT col1
FROM T1
WHERE ROW_NUMBER() OVER (ORDER BY col1) <= 3
AND col1 > 'B'
Run Code Online (Sandbox Code Playgroud)

什么是正确的结果?您是否期望col1 > 'B'谓词在行编号之前或之后运行?

  • @RaviR 他们选择了这样的规则。窗口函数几乎在其他一切之后运行。这就是你不能在 WHERE 中使用它们的原因 (2认同)

Tar*_*ryn 11

不需要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
Run Code Online (Sandbox Code Playgroud)

编辑,将我的评论移至答案.

在实际选择了在WHERE子句之后的数据之前,不执行窗口函数.因此,如果您尝试使用row_numberin WHERE子句中的值尚未分配.

  • @ChristopherPfohl根据我的理解,在选择记录之前不会分配`row_number`,所以你不能在`WHERE`子句中使用它,因为该值还不存在. (3认同)

Luk*_*zda 8

首先,它被称为 all-at-once operation

"一次性操作"意味着同一逻辑查询处理阶段中的所有表达式在逻辑上同时进行评估.

伟大章节对窗口功能的影响:

假设你有:

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;
Run Code Online (Sandbox Code Playgroud)

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.您可以更多地考虑这一点,并找到为什么允许在SELECTORDER BY子句中使用Window Functions !


附录

Terradata支持QUALIFY条款:

根据用户指定的搜索条件过滤先前计算的有序分析函数的结果.

SELECT Id
FROM #Test
WHERE Id = 1002
QUALIFY ROW_NUMBER() OVER(ORDER BY Id) = 1;
Run Code Online (Sandbox Code Playgroud)


Kha*_*han 5

不一定需要使用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
Run Code Online (Sandbox Code Playgroud)