【问题标题】:Reduce time of MySQL JOIN statement execution减少 MySQL JOIN 语句执行的时间
【发布时间】:2016-02-18 16:52:01
【问题描述】:

我有一个类似于 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 

它应该只显示我登录的这个特定用户的通知。但是现在我的通知表上有超过 22500 条记录,而且我总是收到“超出最大执行时间”错误。

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

编辑:添加表格概览

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 ;

【问题讨论】:

  • 使用explain select ... 查看执行计划并将表的索引添加到问题中
  • 我想你是通过主键加入的。你的表中有索引吗? n.type 可以被索引。但是22500条记录并不多。
  • @Kiwi Juicer:每个id 字段都被索引为主键。 @juergen d:我现在真的不知道该怎么做,也不知道应该在哪里做。我可以在 PhpMyAdmin 中这样做吗?
  • 只需要加入性别的用户表吗?可能按 id 而不是日期排序?
  • @RolandStarke:是的。我还删除了这个,看看这是否花费了太多时间,但它没有改变任何东西。

标签: php mysql left-join jointable


【解决方案1】:

将语句拆分为一对 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 表的 uid 字段上添加索引

在 notification_read 表的 uid 字段上添加索引

【讨论】:

    猜你喜欢
    • 2012-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多