RANK OVER PARTITION BY 在聚合函数上

Posted

技术标签:

【中文标题】RANK OVER PARTITION BY 在聚合函数上【英文标题】:RANK OVER PARTITION BY on aggregate functions 【发布时间】:2017-12-11 21:16:06 【问题描述】:

这是一组 3 个表:

订单 (ORDER_ID, CUSTOMER_ID, ORDER_DATE)

订单详情 (ORDER_ID, PRODUCT_ID, PRICE, UNITS)

客户 (CUSTOMER_ID, SIGNUP_DATE, COUNTRY)

我需要按收入确定每个国家/地区的前 3 个月。 这是我想到的查询:

SELECT c.country
    , MONTH(o.order_date) as _Month
    , SUM(od.price * od.units) AS revenue
    , RANK() OVER (PARTITION BY country, _Month ORDER BY revenue) AS Rank  
FROM orders o 
LEFT JOIN customers c ON o.customer_id = c.customer_id  
JOIN order_detail od 
WHERE o.order_id = od.order_id GROUP BY country, Month
HAVING Rank <= 3

此查询是否有效,因为我不确定 SUM(od.price * od.units) as revenue 是否可以在 RANK() OVER 函数中使用,因为它是一个聚合函数?

【问题讨论】:

您使用的是什么数据库?只有 mysql 8.0 支持窗口函数。 @GordonLinoff 不幸的是我没有数据库设置,但我正在练习 MySQL。 您是按每个国家/地区前 3 个月的收入排序,还是按每个国家/地区前 3 个月的收入排序? @Shawn 我按每个国家/地区前 3 个月的收入排序 您有正在使用的示例数据吗? 【参考方案1】:

在支持窗口函数的数据库中,这通常写成:

SELECT cm.*
FROM (SELECT c.country, MONTH(o.order_date) as Month, SUM(od.price * od.units) AS revenue,
             RANK() OVER (PARTITION BY country, MONTH(o.order_date) ORDER BY SUM(od.price * od.units) ) AS Rank 
      FROM orders o LEFT JOIN
           customers c
           ON o.customer_id = c.customer_id  JOIN
           order_detail od 
           ON o.order_id = od.order_id
      GROUP BY country, Month
     ) cm
HAVING Rank <= 3;

【讨论】:

如果我只写RANK() OVER (PARTITION BY country, Month ORDER BY revenue ) AS Rank会有效 @DigvijaySawant 。 . .不是在大多数数据库中,除非您在一个名为 month 的表中有一个列。 知道了。所以别名在 RANK() 函数中不起作用,但它们在 group by 中很好。非常感谢!! 不完全。别名确实在分析功能中起作用,但与大多数地方一样,与创建它们的级别不同。选择 ... RANK() OVER (PARTITION BY country, _Month ORDER BY income) AS Rank from (select ... , MONTH(o.order_date) as _Month , SUM(od.price * od.units) AS 收入, . ..) @GordonLinoff GROUP BY country, Month 不需要为GROUP BY country, MONTH(o.order_date),因为Month 别名对GROUP BY 不可用?而不是HAVING Rank &lt;= 3,应该是WHERE Rank &lt;= 3,因为外部选择中没有分组?不确定这是否是 MySQL 语法。【参考方案2】:

这意味着更多的是评论而不是答案,因为我可以在这里设置更漂亮的格式,但是典型的查询逻辑操作顺序大致如下:

FROM 首先构建。 JOINs 将被应用。 WHERE 会导致以上表格被过滤 GROUP BYaggregates 将被应用。 HAVING 已应用。 WINDOW FUNCTIONs 将被计算。这些在ORDER BY 之外运行 SELECT 将被应用。 DISTINCT 将被应用。 ORDER BY 将被应用。 TOP/LIMIT 将被应用。

一般来说,一个步骤中发生的任何事情都将对其后面的步骤可用(即SELECT 别名可以在ORDER BY 中使用,但不能在GROUP BY 中使用。但在MySQL 中,@987654341 @ 可以使用在SELECT 中定义的别名。当然,优化器总是可以决定做自己的事情,并且可以选择不同的顺序,但这在大多数 SQL 风格中通常会发生。

我的一些参考资料与 MS SQL 相关,但在大多数情况下,仍然适用于其他类型:

https://blog.sqlauthority.com/2009/10/10/sql-server-download-logical-query-processing-poster/

Confused about Itzik Ben-Gan's Logical Query Processing order in his SQL Server 2005 book and SQL Server 2008 book

https://accessexperts.com/wp-content/uploads/2015/07/Logical-Query-Processing-Poster.pdf

https://www.brentozar.com/archive/2015/07/logical-query-processing/

【讨论】:

【参考方案3】:

我不认为 Gordon Linoff 的回答是正确的。

(1) Rank()函数中不能直接调用别名,Gordon已经提到 (2) 对于您的用例,您想对每个国家/地区的月份进行排名,排名部分应该是

RANK() OVER (PARTITION BY country ORDER BY SUM(od.price * od.units) ) AS Rank

【讨论】:

以上是关于RANK OVER PARTITION BY 在聚合函数上的主要内容,如果未能解决你的问题,请参考以下文章

SQL 分析函数:rank() over partition by not working property

MySQL:与dense_rank()over()的group by和partition的差异输出?

Oracle - SELECT DENSE_RANK OVER(ORDER BY、SUM、OVER 和 PARTITION BY)

RANK OVER PARTITION BY 在聚合函数上

OVER(PARTITION BY) 函数

oracle查询中over(partition by ...order by ...)用法