减少 MySQL JOIN 语句执行的时间

Reduce time of MySQL JOIN statement execution

我有一个类似 Facebook 的通知系统,使用以下 MySQL 语句:

SELECT  
n.`id`,n.`content_id`,n.`site_id`,n.`creator_uid`,n.`type`,
nu.`id` AS nuid, nu.`uid` AS nu_uid, nu.`date`,
nr.`id` AS nrid, nr.`uid` AS nr_uid, nr.`is_read`,
u.`gender`

FROM `notification` AS n
LEFT JOIN `notification_user` AS nu ON nu.`nid` = n.`id`
LEFT JOIN `notification_read` AS nr ON nr.`nid` = n.`id` 
LEFT JOIN `users` AS u ON u.`id` = nu.`uid` 
WHERE 
    nu.`uid` != '".$_SESSION['uid']."' AND nr.`uid` = '".$_SESSION['uid']."'
    OR
    (
    nu.`uid` = '".$_SESSION['uid']."' AND n.`type` = 'credits'
    )
ORDER BY date DESC, nu.`id` DESC 

它应该只显示我登录的这个特定用户的通知。但是现在我在通知 table 上有超过 22500 条记录,而且我总是收到 "maximum execution time exceeded" 错误。

我能否以某种方式更改此查询以减少获取所需记录的时间?也许删除连接并执行更多查询?

编辑:添加了Table概述

CREATE TABLE IF NOT EXISTS `notification` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `content_id` int(11) NOT NULL,
  `site_id` int(11) NOT NULL,
  `creator_uid` int(11) NOT NULL,
  `type` varchar(30) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=22759 ;

.

CREATE TABLE IF NOT EXISTS `notification_read` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `nid` int(11) NOT NULL,
  `uid` int(11) NOT NULL,
  `is_read` tinyint(4) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `nid` (`nid`),
  KEY `nid_2` (`nid`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=45342 ;

.

CREATE TABLE IF NOT EXISTS `notification_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `nid` int(11) NOT NULL,
  `uid` int(11) NOT NULL,
  `date` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=22813 ;

将语句拆分为一对 SELECT,并将结果联合在一起:-

(SELECT  
n.`id`,n.`content_id`,n.`site_id`,n.`creator_uid`,n.`type`,
nu.`id` AS nuid, nu.`uid` AS nu_uid, nu.`date`,
nr.`id` AS nrid, nr.`uid` AS nr_uid, nr.`is_read`,
u.`gender`
FROM `notification` AS n
INNER JOIN `notification_user` AS nu ON nu.`nid` = n.`id`
LEFT JOIN `notification_read` AS nr ON nr.`nid` = n.`id` 
LEFT JOIN `users` AS u ON u.`id` = nu.`uid` 
WHERE nu.`uid` = '".$_SESSION['uid']."' AND n.`type` = 'credits')
UNION
(SELECT  
n.`id`,n.`content_id`,n.`site_id`,n.`creator_uid`,n.`type`,
nu.`id` AS nuid, nu.`uid` AS nu_uid, nu.`date`,
nr.`id` AS nrid, nr.`uid` AS nr_uid, nr.`is_read`,
u.`gender`
FROM `notification` AS n
LEFT JOIN `notification_user` AS nu ON nu.`nid` = n.`id`
INNER JOIN `notification_read` AS nr ON nr.`nid` = n.`id` 
LEFT JOIN `users` AS u ON u.`id` = nu.`uid` 
WHERE nu.`uid` != '".$_SESSION['uid']."' AND nr.`uid` = '".$_SESSION['uid']."')
ORDER BY date DESC, nu.`id` DESC 

这应该允许 MySQL 在查询的每个部分有效地使用索引。查询的第一部分需要一条 notification_user 记录,因此您可以在那里使用 INNER JOIN,而第二部分需要一条 notification_read 记录,因此您可以在那里使用 INNER JOIN。这两个都应该减少要处理的行数。

在 notification_user table

上的 uid 字段上添加索引

在 notification_read table

上的 uid 字段上添加索引