此查询非常慢。需要9到10秒......
SELECT DISTINCT a.*
FROM addresses a
LEFT JOIN contacts c
ON c.id = a.contact_id
LEFT JOIN organizations o
ON o.id = a.organization_id
ORDER BY c.last_name, c.first_name, o.name
LIMIT 0, 24
如果我注释掉ORDER BY
子句,查询运行得更快 - 大约5毫秒。但我需要ORDER BY
来支持搜索结果的分页。用户需要通过联系和组织对地址进行排序。
表格结构
addresses
---------
id int NOT NULL
contact_id int # could be NULL
organization_id int # could be NULL
contacts
--------
id int NOT NULL
first_name varchar(255)
last_name varchar(255)
organizations
-------------
id int NOT NULL
name varchar(255)
他们都是InnoDB表。
我在联系人表格上有这些索引:
KEY `idx_contacts_first_name` (`first_name`),
KEY `idx_contacts_last_name` (`last_name`),
KEY `idx_contacts_first_name_last_name` (`first_name`,`last_name`)
在组织表上:
KEY `idx_organization_name` (`name`)
数据量
Addresses: 22,271
Contacts: 17,906
Organizations: 8,246
DESCRIBE输出
mysql> DESCRIBE
-> SELECT DISTINCT a.*
-> FROM addresses a
-> LEFT JOIN contacts c
-> ON c.id = a.contact_id
-> LEFT JOIN organizations o
-> ON o.id = a.organization_id
-> ORDER BY c.last_name, c.first_name, o.name
-> LIMIT 0, 24;
+----+-------------+-------+--------+---------------+---------+---------+--------------------------------------------+-------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+--------------------------------------------+-------+---------------------------------+
| 1 | SIMPLE | a | ALL | NULL | NULL | NULL | NULL | 22387 | Using temporary; Using filesort |
| 1 | SIMPLE | c | eq_ref | PRIMARY | PRIMARY | 4 | contactdb_v2_development.a.contact_id | 1 | Distinct |
| 1 | SIMPLE | o | eq_ref | PRIMARY | PRIMARY | 4 | contactdb_v2_development.a.organization_id | 1 | Distinct |
+----+-------------+-------+--------+---------------+---------+---------+--------------------------------------------+-------+---------------------------------+
3 rows in set (0.00 sec)
答案 0 :(得分:2)
我尝试了你的例子,数据量相近,而在我的低端笔记本电脑(Pentium M 1,7 GHz)上,查询只需不到一秒钟(首次运行,后来运行甚至更少)。
你是不是偶然忘记了id列上的PK?你没有提到它,所以只是问......如果你忘了这一点,表现显然会很糟糕 - 更不用说每个DBA都会在没有PK的桌子上畏缩。
否则,试试这个:
DESCRIBE <your query>
这将为您提供MySQL的查询计划。发布(编辑你的问题),它应该更清楚地花了这么长时间。
进一步思考:
查询将始终存在问题性能,因为您要求数据库读取所有地址并对其进行排序并显示它们。 ORDER BY意味着它必须在给出任何东西之前读取所有东西,所以它总是很慢。这样整个数据库的重叠是什么意思呢?用户会翻阅几千条记录吗?
考虑例如允许搜索查询。使用WHERE条件,查询会更快。
答案 1 :(得分:1)
如果你在服务器端没有太多资源限制而且这个东西不会扩展得太远,你就没有很多数据,所以你可以简单地在那个级别进行排序和分页
答案 2 :(得分:1)
尝试添加此索引:
idx_contacts_last_name_first_name
(last_name
,first_name
)
BTW:您可以删除idx_contacts_first_name,因为它是重复的,如果您添加此索引,则可以删除idx_contacts_last_name。
答案 3 :(得分:1)
尝试将SQL更改为以下内容:
SELECT a.column1, a.column2, ...
FROM addresses a
LEFT JOIN contacts c
ON c.id = a.contact_id
LEFT JOIN organizations o
ON o.id = a.organization_id
GROUP BY a.column1, a.column2, ...
ORDER BY c.last_name, c.first_name, o.name
LIMIT 0, 24
我发现GROUP BY
一般比DISTINCT
要快得多,但我不知道为什么会这样。{/ p>
答案 4 :(得分:0)
让我们看看。
解决LEFT JOIN联系人c为大约20,000个结果提供大约20,000 * 20,000~4亿的比较
LEFT JOIN组织为大约20,000个结果提供了大约10,000 * 20,000~2亿的比较
我们主要对联系行进行排序,然后丢弃除24个之外的所有行。似乎地址的清晰度至关重要。
由于我们主要按联系人排序,我们如何对联系人进行子选择,保留 比我们需要的更多(比如大约4倍):
SELECT * FROM contacts ORDER BY last_name, first_name LIMIT 100
然后加入他们的地址,保持前百名左右
SELECT a.*
FROM (SELECT * FROM contacts ORDER BY last_name, first_name LIMIT 0, 100) AS c
LEFT JOIN addresses a
ON c.id = a.contact_id
LIMIT 0, 100
然后将这些加入组织
SELECT *
FROM (
SELECT *
FROM (SELECT * FROM contacts ORDER BY last_name, first_name LIMIT 0, 100) AS c
LEFT JOIN addresses a
ON c.id = a.contact_id
LIMIT 0, 100
) AS ca LEFT JOIN organizations o
ON o.id = ca.organization_id
ORDER BY ca.last_name, ca.first_name, o.name
LIMIT 0, 24
我确信语法被搞砸了,但我同样确信在每个阶段减少结果集的原则都是一种有益的方式。我也可能做了几次折衷,结果非常接近10秒的答案,但更快地到达那里。