2015-04-05 64 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