Over() 函数未按预期覆盖所有行

Over() function does not cover all rows as expected

我一直在练习 SQL,遇到了这种我无法解释的行为。 (我也是问这个问题的人:)-> 这是一个不同的问题。

假设我有一个这样的table

电影评级table:

movie_id user_id rating created_at
1 1 3 2020-01-12
1 2 4 2020-02-11
1 3 2 2020-02-12
1 4 1 2020-01-01
2 1 5 2020-02-17
2 2 2 2020-02-01
2 3 2 2020-03-01
3 1 3 2020-02-22
3 2 4 2020-02-25

我想做的是按评分对电影进行排名,我有这个 SQL 查询:

SELECT
  movie_id,
  rank() over(partition by movie_id order by avg(rating) desc) as rank_rate
FROM
  MovieRating

从我之前的问题中,我了解到 over() 函数将在查询选择的 window 中运行,基本上是 window this查询 returns:

SELECT movie_id FROM MovieRating

所以我希望在这里至少看到 3 行,对于 id 1、2 和 3。

但是结果只有一行:

{"headers": ["movie_id", "rank_rate"], "values": [[1, 1]]}

这是为什么?我对 over() 函数如何工作的理解有问题吗?

我想你的意思是为每部电影获取一行,以及它的平均评分。

您应该使用 GROUP BY,而不是 window 函数:

SELECT movie_id, AVG(rating) AS avg_rating
FROM MovieRating
GROUP BY movie_id
ORDER BY avg_rating DESC;

https://www.db-fiddle.com/f/o9qLFbJEwhaHDWoTS9Qfwp/1


你只有一行的原因是当你使用像 AVG() 这样的聚合函数时,它隐式地使查询成为聚合查询。查询结果为每组一行。

https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html 说:

If you use an aggregate function in a statement containing no GROUP BY clause, it is equivalent to grouping on all rows.

换句话说,如果您使用 AVG() 但不指定 GROUP BY 表达式,则整个 table 被视为一个“组”。因为整个table是一组,所以结果是一行。

由 windowing 函数定义的

Windows 与聚合函数定义的组不同。 window 函数在通过聚合减少行之后应用。由于只有一组,因此结果中只有一行,因此排名为 1。

您需要聚合查询并对其结果使用 RANK() window 函数:

SELECT movie_id,
       AVG(rating) AS average_rating, -- you may remove this line if you don't actually need the average rating
       RANK() OVER (ORDER BY AVG(rating) DESC) AS rank_rate
FROM MovieRating
GROUP BY movie_id
ORDER BY rank_rate;

参见demo

您的查询是没有 group by 子句的聚合查询,这意味着它对整个 table 而不是每个 movie_id 进行操作。此类查询 return 只有 1 行具有聚合结果。
当你应用 RANK() window 函数时,它将对该单行进行操作,而不是对 table.