Sql 有没有办法加快这个查询的速度?
这个查询真的很慢。需要9到10秒Sql 有没有办法加快这个查询的速度?,sql,mysql,Sql,Mysql,这个查询真的很慢。需要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 如果我注释掉orderby子句,查询运行速度会快得多——大约5毫秒。但是我需要ord
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
如果我注释掉orderby
子句,查询运行速度会快得多——大约5毫秒。但是我需要orderby
来支持搜索结果的分页。用户需要按联系人和组织对地址进行排序
表格结构
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
描述输出
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)
如果您在服务器端没有太多的资源限制,并且这件事不会扩大太多,那么您没有太多的数据,因此您可以简单地在该级别进行排序和分页。如果您在服务器端没有太多的资源限制,并且这件事不会扩大太多,您没有太多的数据,因此您可以简单地在该级别进行排序和分页。尝试添加此索引:
idx\u联系人姓氏姓氏(last\u name
,first\u name
)
顺便说一句:您可以删除idx联系人的名字,因为它是重复的,如果添加此索引,您可以删除idx联系人的姓氏。尝试添加此索引:
idx\u联系人姓氏姓氏(last\u name
,first\u name
)
顺便说一句:您可以删除idx\U联系人的名字,因为它是重复的,如果添加此索引,您可以删除idx\U联系人的姓氏。尝试将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
我发现一般来说,groupby
比DISTINCT
快得多,但我不知道为什么会这样。尝试将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
我发现一般来说,groupby
比DISTINCT
快得多,但我不知道为什么。我用相似的数据量尝试了你的例子,在我的低端笔记本电脑(奔腾M 1,7GHz)上,查询只需不到一秒钟(第一次运行时,以后运行时甚至更少)
你是不是碰巧忘记了id栏上的PK?你没提,所以只要问一下。。。如果您忘记了这一点,性能显然会很糟糕——更不用说每个DBA都会在没有PK的情况下畏缩
否则,请尝试以下操作:
DESCRIBE <your query>
描述
这将为您提供MySQL的查询计划。张贴(编辑你的问题),它应该更清楚什么花了这么长时间
进一步考虑:
查询的性能总是有问题,因为您要求数据库读取和排序所有地址并显示它们。订单意味着它必须在归还任何东西之前阅读所有内容,所以它总是很慢。这样放置整个数据库有什么意义?用户会翻阅几千条记录吗
例如,考虑允许搜索查询。在WHERE条件下,查询速度会快得多。我用相似的数据量尝试了您的示例,在我的低端笔记本电脑(奔腾M 1,7 GHz)上,查询只需不到一秒钟(第一次运行时,以后运行得更少)
你是不是碰巧忘记了id栏上的PK?你没提,所以只要问一下。。。如果您忘记了这一点,性能显然会很糟糕——更不用说每个DBA都会在没有PK的情况下畏缩
否则,请尝试以下操作:
DESCRIBE <your query>
描述
这将为您提供MySQL的查询计划。张贴(编辑你的问题),它应该更清楚什么花了这么长时间
进一步考虑:
查询的性能总是有问题,因为您要求数据库读取和排序所有地址并显示它们。订单意味着它必须在归还任何东西之前阅读所有内容,所以它总是很慢。这样放置整个数据库有什么意义?用户会翻阅几千条记录吗
例如,考虑允许搜索查询。使用WHERE条件,查询速度会快得多。让我们看看
- 地址:22271
- 联系人:17906
- 组织:8246
地址一个左连接联系人c给出了大约20000*20000~40000000个比较结果
LEFT JOIN organizations为大约20000个结果提供了大约10000*20000~200万个比较
我们主要对联系人行进行排序,然后丢弃除24行以外的所有联系人行。似乎地址的清晰性是最不重要的
由于我们主要是按联系人排序,我们对联系人进行子选择,保持
比我们需要的多一点(比如,大约4倍):
SELECT * FROM contacts ORDER BY last_name, first_name LIMIT 100
然后将这些加入到他们的地址中,保留前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秒的答案,但要快得多。让我们看看
- 地址:22271
- 联系人:17906
- 组织:8246
地址一个左连接联系人c给出了大约20000*20000~40000000个比较结果
LEFT JOIN organizations为大约20000个结果提供了大约10000*20000~200万个比较
我们主要对联系人行进行排序,然后丢弃除24行以外的所有联系人行。似乎地址的清晰性是最不重要的
由于我们主要是按联系人排序,我们对联系人进行子选择,保持
比我们需要的多一点(比如,大约4倍):
SELECT * FROM contacts ORDER BY last_name, first_name LIMIT 100
然后将这些加入到他们的地址中,保留前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
我确信语法是错误的,但我同样确信原则是正确的