繁体   English   中英

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

[英]Why no windowed functions in where clauses?

标题说明了一切,为什么我不能在 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 子句中使用窗口函数的原因。

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

一个答案,虽然不是特别有用,是因为规范说你不能。

请参阅 Itzik Ben Gan 撰写的文章 - 逻辑查询处理:它是什么以及它对您意味着什么,特别是此处的图像 在处理完所有WHERE / JOIN / GROUP BY / HAVING子句(步骤 5.1)后剩余的结果集上执行SELECT时,将评估窗口函数。

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

WHERE子句中不允许使用它们的原因是它会造成歧义。 使用窗口函数的高性能 T-SQL 中窃取 Itzik Ben Gan 的示例 (p.25)

假设你的桌子是

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'谓词在行编号之前或之后运行?

不需要 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子句之后实际选择数据之前,不会执行窗口函数。 因此,如果您尝试在WHERE子句中使用row_number ,则该值尚未分配。

“一次性操作”是指同一逻辑查询过程阶段的所有表达式同时进行逻辑求值。

和伟大的一章对窗口函数的影响

假设你有:

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 子句中使用窗口函数。 您可以对此进行更多思考,并找出为什么只允许在SELECTORDER BY子句中使用窗口函数!


附录

Teradata 支持QUALIFY子句:

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

SELECT Id
FROM #Test
WHERE Id = 1002
QUALIFY ROW_NUMBER() OVER(ORDER BY Id) = 1;

附录 2:

雪花 - 合格

QUALIFY 处理窗口函数就像 HAVING 处理聚合函数和 GROUP BY 子句一样。

在查询的执行顺序中, QUALIFY 因此在计算窗口函数之后进行评估。 通常,SELECT 语句的子句按如下所示的顺序进行评估:

 Where Group by Having Window QUALIFY Distinct Order by Limit

不一定需要使用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

这是一个旧线程,但我会尝试具体回答主题中表达的问题。

为什么 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子句在底层查询结果集中定义了一个窗口或用户指定的一组行,并且窗口函数根据该窗口计算结果。

Msg 4108, Level 15, State 1, …
Windowed functions can only appear in the SELECT or ORDER BY clauses.

背后的原因是因为逻辑查询处理T-SQL 由于底层查询结果只有在逻辑查询处理到达SELECT步骤 5.1 时才建立。 (即,在处理FROMWHEREGROUP BYHAVING步骤之后),窗口函数只允许在查询的SELECTORDER BY子句中使用。

请注意,即使关系模型不处理有序数据,窗口函数仍然是关系层的一部分。 SELECT步骤 5.1 后的结果。 与任何窗口函数仍然是相关的。

另外,严格来说, WHERE子句中不允许使用窗口函数并不是因为它会产生歧义,而是因为逻辑查询处理T-SQL处理SELECT语句的顺序。

链接: 这里这里这里

最后,还有老式的、pre-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
)

我给你这个只是为了完整性。

基本上第一个“WHERE”子句条件是由 sql 读取的,并且相同的列/值 id 查看了表,但在表中 row_num=1 仍然不存在。 因此它不会工作。 这就是我们首先使用括号的原因,然后我们将编写 WHERE 子句。

是的,不幸的是,当您执行窗口函数时,即使您的 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.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM