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.
我一直在练习 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是一组,所以结果是一行。
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.