2015-04-05 69 views
0
页面

GROUP BY子句中下面的查询减慢页面,请帮忙解决这个问题MySQL的 - GROUP BY减慢

SELECT 
    `a`.*, 
    CONCAT(a.`firstname`, " ", a.`lastname`) AS `cont_name`, 
    CONCAT(a.`position`, "/", a.`company`) AS `comp_pos`, 
    CONCAT(f.`firstname`, " ", f.`lastname`) AS `created_by` 
FROM 
    `contacts` AS `a` 
    LEFT JOIN `users` AS `f` ON f.id = a.user_id 
    LEFT JOIN `user_centres` AS `b` ON a.user_id = b.user_id 
WHERE b.centre_id IN (23, 24, 25, 26, 20, 21, 22, 27, 28) 
GROUP BY `a`.`id` 
ORDER BY `a`.`created` desc 

这里加入与user_centres表是数据中心的明智过滤。 EXPLAIN给出结果为:

- 1 SIMPLE a index PRIMARY,user_id,area_id,industry_id,country PRIMARY 4 NULL 20145 Using temporary; Using filesort 

我们的要求是如下

  1. 上市的管理员登录的所有联系人

  2. 中心经理接触的明智上市/业务员登录

联系人表中的记录总计> 20K。

user_centres表中的用户将有多个条目,即:用户被分配到多个中心。

通过排除GROUP BY在服务器中执行查询时,有近300k的数据导致了问题。

为表

Db的stucture

表结构contacts

CREATE TABLE IF NOT EXISTS `contacts` (
`id` int(11) NOT NULL, 
`user_id` int(11) DEFAULT NULL, 
`imported` tinyint(4) NOT NULL DEFAULT '0', 
`situation` char(10) DEFAULT NULL, 
`firstname` varchar(150) DEFAULT NULL, 
`lastname` varchar(150) DEFAULT NULL, 
`position` varchar(150) DEFAULT NULL, 
`dob` datetime DEFAULT NULL, 
`office_contact` varchar(100) DEFAULT NULL, 
`mobile_contact` varchar(100) DEFAULT NULL, 
`email` varchar(255) NOT NULL, 
`company` varchar(150) DEFAULT NULL, 
`industry_id` int(11) DEFAULT NULL, 
`address` varchar(255) DEFAULT NULL, 
`city` varchar(150) DEFAULT NULL, 
`country` int(11) DEFAULT NULL, 
`isclient` tinyint(4) NOT NULL DEFAULT '0', 
`classification` varchar(100) DEFAULT NULL, 
`created` datetime NOT NULL, 
`updated` datetime NOT NULL, 
`unsubscribe` enum('Y','N') NOT NULL DEFAULT 'N' 
) ENGINE=InnoDB AUTO_INCREMENT=25203 DEFAULT CHARSET=latin1; 

指标的表contacts

ALTER TABLE `contacts` 
ADD PRIMARY KEY (`id`), ADD KEY `user_id` (`user_id`), 
ADD KEY `industry_id` (`industry_id`), ADD KEY `country` (`country`); 

约束的表contacts

对于表

表结构users

CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL, 
`role_id` int(11) NOT NULL, 
`email` varchar(250) NOT NULL, 
`password` varchar(45) NOT NULL, 
`salt` varchar(45) DEFAULT NULL, 
`status_id` int(11) DEFAULT NULL, 
`status` tinyint(1) DEFAULT '1', 
`firstname` varchar(255) NOT NULL, 
`lastname` varchar(255) NOT NULL, 
`created` datetime DEFAULT NULL, 
`updated` datetime DEFAULT NULL 
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=latin1; 

指标的表users

ALTER TABLE `users` 
ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `email_UNIQUE` (`email`), 
ADD KEY `type_id_idx` (`role_id`), ADD KEY `status_id_idx` (`status_id`); 

约束的表users

ALTER TABLE `users` 
ADD CONSTRAINT `role_id` FOREIGN KEY (`role_id`) 
REFERENCES `users_roles` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 
ADD CONSTRAINT `status_id` FOREIGN KEY (`status_id`) 
REFERENCES `users_status` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 
ADD CONSTRAINT `users_ibfk_1` FOREIGN KEY (`area`) 
REFERENCES `area` (`id`) ON DELETE SET NULL ON UPDATE NO ACTION; 

表结构表user_centres

CREATE TABLE IF NOT EXISTS `user_centres` (
`id` int(11) NOT NULL, 
`user_id` int(11) NOT NULL, 
`area_id` int(11) NOT NULL, 
`centre_id` int(11) NOT NULL 
) ENGINE=InnoDB AUTO_INCREMENT=72 DEFAULT CHARSET=utf8; 

指数为表user_centres

ALTER TABLE `user_centres` 
ADD PRIMARY KEY (`id`), ADD KEY `user_id` (`user_id`), 
ADD KEY `centre_id` (`centre_id`), ADD KEY `area_id` (`area_id`); 

约束的表user_centres

ALTER TABLE `user_centres` 
ADD CONSTRAINT `user_centres_ibfk_1` FOREIGN KEY (`user_id`) 
REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION, 
ADD CONSTRAINT `user_centres_ibfk_2` FOREIGN KEY (`centre_id`) 
REFERENCES `centre` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION; 

请同时参阅EXPLAIN屏幕 - 因为不同的ORDER BY一个的http://prntscr.com/6o5h8s

+0

您滥用非标准MySQL扩展到'GROUP BY',你很可能被误用'集团BY'本身。请阅读这个。 https://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html问题:你的'contacts'和'users'表之间的关系是什么?它是1:1吗? – 2015-04-06 01:12:30

回答

0

指数未使用GROUP BY子句。

http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html 
+0

嗨Manadh, 感谢您的快速响应,我如何在这里实现排序。在这里,点击联系人列表中字段标题的动态字段进行排序。 – 2015-04-05 05:54:11

0

你花了很多时间检查user_centres,但不需要任何东西。从查询中删除它。

users可以做成一个相关因素子查询:

SELECT `a`.*, 
     CONCAT(a.`firstname`, " ", a.`lastname`) AS `cont_name`, 
     CONCAT(a.`position`, "/", a.`company`) AS `comp_pos`, 
     (SELECT CONCAT(f.`firstname`, " ", f.`lastname`) 
      FROM `users` AS `f` ON f.id = a.user_id 
     ) AS `created_by` 
    FROM `contacts` AS `a` 
    GROUP BY `a`.`id` 
    ORDER BY `a`.`created` desc 

你真的需要所有的20K行?结果的绝大部分是缓慢的一部分。

+0

在我看来像他想要过滤基于'user_centres'中的列的结果集。 – 2015-04-06 01:07:27

+1

但是用'LEFT JOIN',是否有匹配并不重要。 – 2015-04-06 01:42:34

+0

是Jones,我需要根据user_centres中的列过滤结果集。这是条件WHERE b.centre_id IN(23,24,25,26,20,21,22,27,28) – 2015-04-06 08:23:35

0

感谢所有的基础上,反馈所有的你有我也试过低于现在的查询,并给我30秒的速度提升至15秒

SELECT `a`.`id`, `a`.`user_id`, `a`.`imported`, `a`.`created`, 
     `a`.`unsubscribe`, CONCAT(a.firstname, " ", a.lastname) AS `cont_name`, 
     CONCAT(a.position, "/", a.company) AS `comp_pos`, 
     (SELECT COUNT(uc.id) 
      FROM `user_centres` AS `uc` 
      WHERE (uc.user_id = a.user_id) 
       AND (uc.centre_id IN (29)) 
      GROUP BY `uc`.`user_id` 
    ) AS `centre_cnt`, 
     (SELECT GROUP_CONCAT(DISTINCT g.group_name 
        ORDER BY g.group_name ASC SEPARATOR ", ") 
      FROM `groups` AS `g` 
      INNER JOIN `group_contacts` AS `gc` ON g.id = gc.group_id 
      WHERE (gc.contact_id = a.id) 
      GROUP BY `gc`.`contact_id` 
    ) AS `group_name`, 
     (SELECT CONCAT(u.`firstname`, " ", u.`lastname`) 
      FROM `users` AS `u` 
      WHERE (u.id = a.user_id) 
    ) AS `created_by`, `e`.`name` AS `industry_name` 
    FROM `contacts` AS `a` 
    LEFT JOIN `industries` AS `e` ON e.id = a.industry_id 
    WHERE (1) 
    HAVING (centre_cnt is NOT NULL) 
    ORDER BY `a`.`id` desc 

让我知道有没有办法来提高速度并使页面加载速度低于5秒。

请参阅接口(注意过滤和排序字段) - http://prntscr.com/6q6q70

+0

现在将'COUNT(uc.id)'改为'COUNT(*)'并在'user_centres'上添加复合索引'INDEX(user_id,centre_id)'。这些更改应将其切换为“使用索引”,速度更快。 – 2015-04-06 18:26:59