我怎样才能优化这个MySQL查询?

时间:2010-10-22 05:45:33

标签: mysql optimization

我有一个存储pupil_id,类别和生效日期(以及其他内容)的表。日期可以是过去,现在或将来。我需要一个查询,从表中提取学生的当前状态。

以下查询有效:

SELECT * 
FROM pupil_status 
WHERE (status_pupil_id, status_date) IN (
    SELECT status_pupil_id, MAX(status_date) 
    FROM pupil_status 
    WHERE status_date < NOW() -- to ensure we ignore the "future status"
    GROUP BY status_pupil_id );

在MySQL中,该表定义如下:

CREATE TABLE IF NOT EXISTS `pupil_status` (
  `status_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `status_pupil_id` int(10) unsigned NOT NULL, -- a foreign key
  `status_category_id` int(10) unsigned NOT NULL, -- a foreign key
  `status_date` datetime NOT NULL, -- effective date/time of status change
  `status_modify` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `status_staff_id` int(10) unsigned NOT NULL, -- a foreign key
  `status_notes` text NOT NULL, -- notes detailing the reason for status change
  PRIMARY KEY (`status_id`),
  KEY `status_pupil_id` (`status_pupil_id`,`status_category_id`),
  KEY `status_pupil_id_2` (`status_pupil_id`,`status_date`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1409 ;

然而,在表格中有950名学生和超过1400种状态,查询需要0.185秒才能处理。也许现在可以接受,但是当桌子膨胀时,我担心可扩展性。生产系统可能有超过10000名学生,每个学生将拥有15-20个状态。

有没有更好的方法来编写此查询?我应该有更好的索引来帮助查询吗?请告诉我。

2 个答案:

答案 0 :(得分:4)

您可以尝试以下方法

1使用INNER JOIN而不是WHERE

SELECT * 
FROM pupil_status ps
INNER JOIN 
    (SELECT status_pupil_id, MAX(status_date) 
    FROM pupil_status 
    WHERE status_date < NOW()
    GROUP BY status_pupil_id) X
ON ps.status_pupil_id = x.status_pupil_id
AND ps.status_date = x.status_date

2有一个变量并存储NOW()的值 - 我不确定数据库引擎是否将此调用优化为NOW()只是一次调用,但如果它没有,那么这可能会有所帮助

这些是一些建议,但是您需要比较查询计划,看看是否有任何明显的改进。 根据您根据查询计划使用的索引,robob的上述建议也可以派上用场

答案 1 :(得分:1)

了解加载系统时查询需要多长时间,每个学生有10000个学生,每个学生有15-20个状态。

如果花费太长时间,只能重构。