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 <= 3
,应该是WHERE Rank <= 3
,因为外部选择中没有分组?不确定这是否是 MySQL 语法。【参考方案2】:
这意味着更多的是评论而不是答案,因为我可以在这里设置更漂亮的格式,但是典型的查询逻辑操作顺序大致如下:
FROM
首先构建。
JOIN
s 将被应用。
WHERE
会导致以上表格被过滤
GROUP BY
和 aggregates
将被应用。
HAVING
已应用。
WINDOW FUNCTION
s 将被计算。这些在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)