【问题标题】:How to optimize this query with repeated subqueries?如何使用重复的子查询优化此查询?
【发布时间】:2013-10-01 16:23:10
【问题描述】:

我有这个查询可以正确回答这个问题: 平均评分最高的电影叫什么名字?

select title 
from (
   select movies.title, avg(rating) avgrating 
   from movies, rentals 
   where movies.movieid = rentals.movieid 
   group by movies.title
) a 
where avgrating = (select max(avgrating) 
                   from (select movies.title, avg(rating) avgrating 
                   from movies, rentals 
                   where movies.movieid=rentals.movieid 
                   group by movies.title) b) 
order by title desc

唯一的问题是在我的大型数据库上运行需要几分钟。我想不出如何让它更快。基本上子查询 a 和 b 是等价的,但据我所知,我必须重复一遍,因为 where 子句看不到“a”子查询。 重要提示:最高平均评分可能会出现平局,并且查询必须返回与平局一样多的标题。 我还应该提到,连接是必要的,因为标题在电影表中,而评分在租借表中。

Movies (
    movieId INTEGER PRIMARY KEY,
    title TEXT,
    year INTEGER
)

Rentals (
    cardNo INTEGER,
    movieId INTEGER,
    date DATE,
    rating INTEGER,
    PRIMARY KEY(cardNo, movieID, date),
    FOREIGN KEY (cardNo) REFERENCES Customers,
    FOREIGN KEY (movieId) REFERENCES Movies
)

【问题讨论】:

  • 哪个数据库? sqlitepostgresql?
  • 电影的主键是什么,movieidtitle

标签: database sqlite postgresql


【解决方案1】:

您可以使用公用表表达式只运行一次子查询:

with avg_ratings as (
   select movies.title, 
          avg(rentals.rating) as avgrating 
   from movies
     join rentals on movies.movieid = rentals.movieid 
   group by movies.title
)    
select title 
from avg_ratings 
where avgrating = (select max(avgrating) from avg_ratings);

这也可以使用窗口函数来编写:

with avg_ratings as (
   select movies.title, 
          avg(rentals.rating) as avgrating 
   from movies
     join rentals on movies.movieid = rentals.movieid 
   group by movies.title
)    
select title
from (
   select title, 
          avgrating,
          dense_rank() over (order by avgrating desc) as rating_rank
   from avg_ratings
) t
where rating_rank = 1;

不确定哪个版本更快。


如果您仍然有性能问题,则需要发布表的定义、它们的索引和执行计划(有关如何发布性能相关问题的更多信息,请参见此处:http://wiki.postgresql.org/wiki/SlowQueryQuestions

【讨论】:

  • 这是不对的。这是返回一些随机标题,而不是最高评价。使用窗口函数的问题在于它返回的行数与原始关系相同。
  • @db-user:我在第一个版本中错误地使用了partition by,请查看我的编辑。我很确定条件where rating_rank = 1 只返回最高评级。我还添加了一个与您的查询匹配但不重复子选择的版本。
  • 这两个在 postgres 上的速度是原来的两倍。但是,sqlite 不支持 with 子句,我需要它在 sqlite 上运行
  • @db-user:抱歉,我没有看到 SQLite 标记。不知道如何在那里调整它(我从未使用过它)。根据我的经验,不存在独立于 DBMS 的语句之类的东西——如果它们真的是,那只意味着它们在所有 DBMS 上同样慢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多