0

我在使用 MySQL 查询时遇到了速度问题。表定义如下:

CREATE TABLE IF NOT EXISTS `student` (
  `student_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `forename` varchar(30) NOT NULL,
  `updated_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `surname` varchar(50) NOT NULL,
  `student_college` int(11) DEFAULT NULL,
  `countup` smallint(5) unsigned DEFAULT NULL, 
  PRIMARY KEY (`student_id`),
  KEY `countup` (`countup`),
  KEY `student_sort` (`countup`,`updated_time`),
  KEY `student_college` (`student_college`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `college` (
  `college_id` int(11) NOT NULL AUTO_INCREMENT,
  `college_name` varchar(100) NOT NULL DEFAULT 'Centre Name',
  `college_location` int(11) DEFAULT NULL,
  PRIMARY KEY (`college_id`),
  KEY `college_location` (`college_location`),
  KEY `college_name` (`college_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

查询如下:

SELECT *
FROM student
JOIN college ON student.student_college = college.college_id
WHERE  
college_location = 1
ORDER BY student.countup desc, student.updated_time desc
LIMIT 15;

我得到以下解释:

id select_type table   type possible_keys              key              key_len    ref                           rows Extra
1  SIMPLE      college ref  "PRIMARY,college_location" college_location 5          const                         915  Using where; Using temporary; Using filesort
1  SIMPLE      student ref  student_college            student_college  5          speed_test.college.college_id 50   Using where

Student 表有大约 500,000 条记录,college 表有 915 行。第三张表用于保存学院的所有位置。我的查询需要检索特定位置的所有学生,然后按 countup 和 updated_time 对结果进行排序。我有一个关于 countup 和 updated_time 的复合索引。我想摆脱文件排序,但我找不到令人满意的方法。

我已经考虑将 移动college_location到学生表中,以便可以将其组合成一个复合索引。有更好的解决方案吗?

4

3 回答 3

4

下面的查询将删除使用临时;使用文件排序。从解释,所以这应该在理论上运行得更好..

MySQL优化器是愚蠢的,所以诀窍是强制优化器想要你想要的,这是一个基于college.college_location = 1的派生表。所以你可以将结果与学生表进行INNER JOIN。这样MySQL可以使用排序键

SELECT 
 *
FROM 
 student
INNER JOIN (
    SELECT 
     college_id
    FROM 
     college
    WHERE
     college.college_location = 1  
  ) college
ON student.student_college = college.college_id
ORDER BY
    student.countup DESC
  , student.updated_time DESC

注意大写锁定中的新索引

见演示http://sqlfiddle.com/#!2/05c8a/1

或者,如果您认为它更有意义或更易于阅读,则可以使用它。性能应该是相同的,因为解释向我解释它是相同的。

SELECT 
 * 
FROM (
  SELECT 
    college_id
  FROM 
    college
  WHERE
    college.college_location = 1  
) 
  college

INNER JOIN
 student 

ON
 student.student_college = college.college_id

ORDER BY
    student.countup DESC
  , student.updated_time DESC

见演示http://sqlfiddle.com/#!2/05c8a/23

新策略分而治之的方法 向数据库发起更多查询,这将利用正确的索引。并消除对临时表和文件排序的需要。

SET @college_ids = NULL; 

SELECT
  GROUP_CONCAT(college_id)
FROM
  college
WHERE
  college_location = 1
GROUP BY
  college_location ASC
INTO @college_ids;

SELECT 
 *
FROM 
 student
WHERE 
 student.student_college IN(@college_ids)
ORDER BY
    student.countup DESC
  , student.updated_time DESC
;

见演示http://sqlfiddle.com/#!2/454b3/61

于 2013-09-12T21:54:33.323 回答
0

我不能很容易地对此进行测试,但请尝试使用此student_sort密钥:

KEYstudent_sort (student_college ,countup DESC ,updated_timeDESC)

于 2013-09-12T12:54:29.550 回答
0

尝试索引:

KEY student_sort(countup DESC ,updated_time DESC)

然后使用 STRAIGHT_JOIN 和 FORCE INDEX:

SELECT *
FROM student force index(student_sort) STRAIGHT_JOIN 
     college 
         ON student.student_college = college.college_id
WHERE college_location = 1
ORDER BY student.countup desc, 
         student.updated_time desc
LIMIT 15;
于 2015-03-23T16:19:13.230 回答