mysql - mysql join的结果按第三个表的平均值排序?

标签 mysql sorting join

我有三张 table 。

一个表包含大约 75,000 行的提交内容
一张表包含提交评分,并且只有 < 10 行
一张表包含提交 => 竞争映射,并且我的测试数据也有大约 75,000 行。

我想做的是

Get the top 50 submissions in a round of a competition. Top is classified as highest average rating, followed by highest amount of votes

这是我正在使用的查询,它有效,但问题是它需要超过 45 秒才能完成!我分析了查询(结果在底部),瓶颈是将数据复制到临时表,然后对其进行排序,那么如何加快速度?

 SELECT `submission_submissions`.* 
   FROM `submission_submissions`
   JOIN `competition_submissions` 
     ON `competition_submissions`.`submission_id` = `submission_submissions`.`id`
LEFT JOIN `submission_ratings` 
     ON `submission_submissions`.`id` = `submission_ratings`.`submission_id`
  WHERE `top_round` =  1 
    AND `competition_id` =  '2'
    AND `submission_submissions`.`date_deleted` IS NULL
GROUP BY submission_submissions.id
ORDER BY AVG(submission_ratings.`stars`) DESC, 
         COUNT(submission_ratings.`id`) DESC
  LIMIT 50

submission_submissions

CREATE TABLE `submission_submissions` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `account_id` int(11) NOT NULL,
  `title` varchar(255) NOT NULL,
  `description` varchar(255) DEFAULT NULL,
  `genre` int(11) NOT NULL,
  `goals` text,
  `submission` text NOT NULL,
  `date_created` datetime DEFAULT NULL,
  `date_modified` datetime DEFAULT NULL,
  `date_deleted` datetime DEFAULT NULL,
  `cover_image` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `genre` (`genre`),
  KEY `account_id` (`account_id`),
  KEY `date_created` (`date_created`)
) ENGINE=InnoDB AUTO_INCREMENT=115037 DEFAULT CHARSET=latin1;

提交评级

CREATE TABLE `submission_ratings` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `account_id` int(11) NOT NULL,
  `submission_id` int(11) NOT NULL,
  `stars` tinyint(1) NOT NULL,
  `date_created` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `submission_id` (`submission_id`),
  KEY `account_id` (`account_id`),
  KEY `stars` (`stars`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;

competition_submissions

CREATE TABLE `competition_submissions` (
  `competition_id` int(11) NOT NULL,
  `submission_id` int(11) NOT NULL,
  `top_round` int(11) DEFAULT '1',
  PRIMARY KEY (`submission_id`),
  KEY `competition_id` (`competition_id`),
  KEY `top_round` (`top_round`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

显示个人资料结果(按持续时间排序)

state                 duration (summed) in sec percentage
Copying to tmp table  33.15621                 68.46924
Sorting result        11.83148                 24.43260
removing tmp table     3.06054                  6.32017
Sending data           0.37560                  0.77563
... insignificant amounts removed ...
Total                  48.42497               100.00000

解释

id  select_type  table                    type         possible_keys                     key                       key_len  ref                                              rows   Extra                                                                                                 
1   SIMPLE       competition_submissions  index_merge  PRIMARY,competition_id,top_round  competition_id,top_round  4,5                                                       18596  Using intersect(competition_id,top_round); Using where; Using index; Using temporary; Using filesort  
1   SIMPLE       submission_submissions   eq_ref       PRIMARY                           PRIMARY                   4        inkstakes.competition_submissions.submission_id  1      Using where                                                                                           
1   SIMPLE       submission_ratings       ALL          submission_id                                                                                                         5      Using where; Using join buffer (flat, BNL join)                                                       

最佳答案

假设实际上您不会对未评级的提交感兴趣,并且给定的提交对于给定的比赛和 top_round 只有一个 competition_submissions 条目,我建议:

SELECT s.* 
FROM (SELECT `submission_id`, 
             AVG(`stars`) AvgStars, 
             COUNT(`id`) CountId
      FROM `submission_ratings` 
      GROUP BY `submission_id`
      ORDER BY AVG(`stars`) DESC, COUNT(`id`) DESC
      LIMIT 50) r
JOIN `submission_submissions` s
  ON r.`submission_id` = s.`id` AND
     s.`date_deleted` IS NULL
JOIN `competition_submissions` c
  ON c.`submission_id` = s.`id` AND 
     c.`top_round` =  1 AND
     c.`competition_id` = '2'
ORDER BY r.AvgStars DESC, 
         r.CountId DESC

(如果给定比赛和 top_round 的每次提交有多个 competition_submissions 条目,那么您可以将 GROUP BY 子句添加回主查询中。)

如果您确实想查看未评级的提交,可以将此查询的结果合并到 LEFT JOIN ... WHERE NULL 查询。

关于mysql - mysql join的结果按第三个表的平均值排序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18170942/

相关文章:

javascript - 旧对象去哪儿了?

mysql - Laravel 和 MySQL JOIN

MySql 将嵌套的 SELECT IN 查询转换为 JOIN

java - 外键抛出 ConstraintViolationException

mySQL - 根据一列中的条件计算不同的行

php - 如何在 php 中逐段读取、存储和获取数据

Mysql DISTINCT account_id 和 ORDER BY

mysql - SQL 按不同字段排序

java - 链接列表 - 如何制作一个递归添加方法来添加 "sort "?

java - 如何按降序打印 HashMap 值,但如果两个或多个值相等,则按键升序打印它们? (JAVA)