我在MySQL 5.6.17中有表departments
,employees
和emails
(对于Rails应用程序)。每个部门都有很多员工,部门和员工都有很多电子邮件。我想根据整个部门和部门内各个员工的电子邮件数量对departments
进行排序。我的尝试:
SELECT departments.*, COUNT(DISTINCT employees.id) AS employees_count, COUNT(DISTINCT emails.id) AS emails_count
FROM departments
LEFT OUTER JOIN employees
ON employees.department_id = departments.id AND employees.is_employed = true
LEFT OUTER JOIN emails
ON (emails.emailable_id = departments.id AND emails.emailable_type = 'department')
OR (emails.emailable_id = employees.id AND emails.emailable_type = 'employee')
GROUP BY departments.id
ORDER BY emails_count DESC
LIMIT 20;
不幸的是,此查询需要3分钟才能完成。由于此查询将在Web界面中使用,因此这不是可行的时间范围。 EXPLAIN
给出了:
+----+-------------+-------------+-------+-------------------------------------------------+----------------------------------+---------+-------------------------------+-------+------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+-------+-------------------------------------------------+----------------------------------+---------+-------------------------------+-------+------------------------------------------------+
| 1 | SIMPLE | departments | index | PRIMARY | PRIMARY | 4 | NULL | 37468 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | employees | ref | index_employees_on_department_id | index_employees_on_department_id | 5 | development_db.departments.id | 5 | Using where |
| 1 | SIMPLE | emails | ALL | index_emails_on_emailable_id_and_emailable_type | NULL | NULL | NULL | 10278 | Range checked for each record (index map: 0x2) |
+----+-------------+-------------+-------+-------------------------------------------------+----------------------------------+---------+-------------------------------+-------+------------------------------------------------+
然后,emails
上的索引未被使用。当我将emails
仅加入departments
或仅加入employees
时,此索引已成功使用,但不会同时加入{{1}}。
这是为什么?我该怎么办?有没有更有效的方法来查询所需的数据?
答案 0 :(得分:1)
在连接之前首先进行聚合可能会有所帮助:
SELECT d.*, e.employees_count, em.emails_count
FROM d LEFT OUTER JOIN
(SELECT e.department_id, count(*) as employees_count
FROM employees e
WHERE e.is_employed = true
GROUP BY e.department_id
) e
ON e.department_id = d.id LEFT OUTER JOIN
(SELECT department_id, count(distinct id) as emails_count
FROM (SELECT em.emailable_id as department_id, em.id
FROM emails em
WHERE em.emailable_type = 'department'
UNION ALL
SELECT e.department_id, em.id
FROM emails em JOIN
employees e
ON em.emailable_id = e.id AND em.emailable_type = 'employee'
) ee
GROUP BY department_id
) em
ON em.department_id = d.id LEFT OUTER JOIN
ORDER BY emails_count DESC
LIMIT 20;
您还需要emails(emailable_id, emailable_type, id)
和emails(emailable_type, emailable_id, id)
上的索引。