sql - 查找某年获奖次数最多的电影——代码重复

标签 sql postgresql aggregate-functions window-functions

我正在尝试编写查询 (PostgreSQL) 以获取“2012 年获奖次数最多的电影”。

我有下表:

CREATE TABLE Award(
    ID_AWARD bigserial CONSTRAINT Award_pk PRIMARY KEY,
    award_name VARCHAR(90),
    category VARCHAR(90),
    award_year integer,
    CONSTRAINT award_unique UNIQUE (award_name, category, award_year));

CREATE TABLE AwardWinner(
    ID_AWARD integer,
    ID_ACTOR integer,
    ID_MOVIE integer,
    CONSTRAINT AwardWinner_pk PRIMARY KEY (ID_AWARD));

我写了下面的查询,它给出了正确的结果,但我认为有很多代码重复。

select * from 
(select id_movie, count(id_movie) as awards 
from Award natural join awardwinner 
where award_year = 2012 group by id_movie) as SUB
where awards = (select max(count) from 
(select id_movie, count(id_movie) 
from Award natural join awardwinner 
where award_year = 2012 group by id_movie) as SUB2);

所以SUBSUB2是完全相同的子查询。有更好的方法吗?

最佳答案

那么你可以使用 common table expression避免代码重复:

with cte_s as (
   select id_movie, count(id_movie) as awards
   from Award natural join awardwinner 
   where award_year = 2012
   group by id_movie
)
select
    sub.id_movie, sub.awards
from cte_s as sub
where sub.awards = (select max(sub2.awards) from cte_s as sub2)

或者你可以用 window function 做这样的事情(未经测试,但我认为 PostgreSQL 允许这样做):

with cte_s as (
    select
        id_movie,
        count(id_movie) as awards,
        max(count(id_movie)) over() as max_awards
    from Award natural join awardwinner 
    where award_year = 2012
    group by id_movie
)
select id_movie
from cte_s
where max_awards = awards

另一种方法是使用 rank()函数(未经测试,可能你必须使用两个 cte 而不是一个):

with cte_s as (
    select
        id_movie,
        count(id_movie) as awards,
        rank() over(order by count(id_movie) desc) as rnk
    from Award natural join awardwinner 
    where award_year = 2012
    group by id_movie
)
select id_movie
from cte_s
where rnk = 1

更新 当我创建这个答案时,我的主要目标是展示如何使用 cte 来避免代码重复。一般来说,如果可能的话,最好避免在查询中多次使用 cte - 第一次查询使用 2 表扫描(或索引查找),第二次和第三次只使用一次,所以我应该指定最好使用这些查询。无论如何,@Erwin 在他的回答中做了这个测试。只是为了补充他的主要观点:

  • 我还建议不要使用自然连接,因为它容易出错。实际上,我的主要 RDBMS 是不支持它的 SQL Server,所以我更习惯于显式 outer/inner join
  • 在查询中始终使用别名是个好习惯,这样可以避免 strange results .
  • 这可能是完全主观的事情,但通常如果我只使用一些表来从查询的主表中过滤掉行(就像在这个查询中,我们只想获得 awards 2012 年,只过滤来自 awardwinner 的行),我不喜欢使用 join,而是使用 existsin相反,这对我来说似乎更合乎逻辑。
所以最终查询可能是:
with cte_s as (
    select
        aw.id_movie,
        count(*) as awards,
        rank() over(order by count(*) desc) as rnk
    from awardwinner as aw
    where
        exists (
            select *
            from award as a
            where a.id_award = aw.id_award and a.award_year = 2012
        )
    group by aw.id_movie
)
select id_movie
from cte_s
where rnk = 1

关于sql - 查找某年获奖次数最多的电影——代码重复,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20021602/

相关文章:

MYSQL 循环函数不起作用

postgresql - 测试 JOOQ postgres jsonb 列是否存在键

sql - 是否可以以这种方式使用 AGG 函数的 SQL 查询?

sql - 在选择查询中循环

ruby-on-rails - 处理事务内的唯一约束异常

MySQL:按条件分组和聚合函数

mysql - 优化排序查询

sql - 分解 SQL 表中每一行的 XML

iphone - Objective-C 中的 MySQL 命令

postgresql - Debian squeeze backports vs PostgreSQL 9.1