【问题标题】:Need to speed up MySQL query, it's at 3+ seconds with only 4,000 records, has 3 joins需要加快 MySQL 查询速度,只有 4,000 条记录,3 秒以上,有 3 个连接
【发布时间】:2013-12-04 19:38:07
【问题描述】:

情况如下:

我有几张表格(如下所述),用于跟踪公寓楼中的居民、他们的单元号以及他们最后一次“被看到”的时间(我们有很多有健康问题的老年人,因此检查他们很重要每 2 天左右;有时它们会死在这里,这就是我们知道如何检查它们的方式。

“检查”的限制条件是必须在过去 48 小时内看到它们;如果没有,查询应该拉他们的记录。以下是我正在使用的表定义:

“人”表,存储居民信息:

MariaDB [olin2]> describe people;
+-------------+-------------+------+-----+-------------------+----------------+
| Field       | Type        | Null | Key | Default           | Extra          |
+-------------+-------------+------+-----+-------------------+----------------+
| id          | int(11)     | NO   | PRI | NULL              | auto_increment |
| fname       | varchar(32) | NO   | MUL | NULL              |                |
| lname       | varchar(32) | NO   |     | NULL              |                |
| dob         | date        | YES  |     | NULL              |                |
| license_no  | varchar(24) | NO   |     | NULL              |                | 
| date_added  | timestamp   | NO   |     | CURRENT_TIMESTAMP |                |
| status      | varchar(8)  | NO   |     | Allow             |                |
| license_exp | date        | YES  |     | NULL              |                |
+-------------+-------------+------+-----+-------------------+----------------+

“单位”表,其中存储单位编号(人们切换单位,所以我不希望他们在“人”表中):

MariaDB [olin2]> describe units;
+----------+-------------+------+-----+---------+----------------+
| Field    | Type        | Null | Key | Default | Extra          |
+----------+-------------+------+-----+---------+----------------+
| id       | int(11)     | NO   | PRI | NULL    | auto_increment |
| number   | varchar(3)  | NO   | MUL | NULL    |                |
| resident | int(11)     | NO   |     | NULL    |                |
| type     | varchar(16) | NO   |     | NULL    |                |
+----------+-------------+------+-----+---------+----------------+

以及存储“支票”的“健康”表(居民的身份证号码、何时以及由谁看到等):

MariaDB [olin2]> describe wellness;
+--------------+-------------+------+-----+-------------------+----------------+
| Field        | Type        | Null | Key | Default           | Extra          |
+--------------+-------------+------+-----+-------------------+----------------+
| wellness_id  | int(11)     | NO   | PRI | NULL              | auto_increment |
| people_id    | int(11)     | NO   |     | NULL              |                |
| time_checked | timestamp   | NO   |     | CURRENT_TIMESTAMP |                |
| check_type   | varchar(1)  | NO   |     | NULL              |                |
| username     | varchar(16) | NO   |     | NULL              |                |
| return_date  | timestamp   | YES  |     | NULL              |                |
+--------------+-------------+------+-----+-------------------+----------------+

“健康”表中的“return_date”字段是针对居民离开超过2天时,显示时不会包含在结果中(实际上会包含在查询结果中) ,但我使用 PHP 将其过滤掉)。

这是我一直在使用的查询...它运行了几个星期,但随着添加的记录越来越多,它变得明显变慢(现在返回结果需要 3.5 秒):

select p.id, w.time_checked, w.username, w.return_date 
from people p 
    left join units u on p.id = u.resident 
    left join wellness w on p.id = w.people_id 
    left join wellness as w2 on w.people_id = w2.people_id 
        and w.time_checked < w2.time_checked
where w2.people_id is null 
    and w.time_checked  < (now() - interval 48 hour)
order by u.number

我知道我的问题是连接,但我不知道如何在没有它们的情况下获得所需的结果和/或如何优化此查询以加快速度...这是结果示例(如果需要) :

+----+---------------------+----------+---------------------+
| id | time_checked        | username | return_date         |
+----+---------------------+----------+---------------------+
|  8 | 2013-12-01 11:00:13 | tluce    | 0000-00-00 00:00:00 |
+----+---------------------+----------+---------------------+
1 row in set (3.44 sec)

所以,在这个结果集中,居民 8 已经有 3 天没有出现了……结果是正确的,但 3.44 秒对于我的用户来说是不可接受的,必须等待。

关于如何改进这一点有什么想法吗?

编辑(更多信息):

我意识到更新每个人的健康条目会更容易、更快捷;但是我喜欢手头有这些数据,因为我从中生成图表以显示 A) 我们最常看到特定居民的时间和 B) 工作人员最常检查人员的时间(也就是 - 谁在做他们的工作,谁不是)

我确实使用了索引,这是我的查询中 EXPLAIN 的结果:

 +------+-------------+-------+--------+---------------+---------+---------+-----------------    -+------+--------------------------------------------------------------------+
| id   | select_type | table | type   | possible_keys | key     | key_len |  ref              | rows | Extra                                                              |
+------+-------------+-------+--------+---------------+---------+---------+-----------------    -+------+--------------------------------------------------------------------+
|    1 | SIMPLE      | u     | ALL    | NULL          | NULL    | NULL    | NULL             |  107 | Using temporary; Using filesort                                    |
|    1 | SIMPLE      | p     | eq_ref | PRIMARY,idx   | PRIMARY | 4       | olin2.u.resident |    1 | Using where                                                        |
|    1 | SIMPLE      | w     | ALL    | NULL          | NULL    | NULL    | NULL             | 7074 | Using where; Using join buffer (flat, BNL join)                    |
|    1 | SIMPLE      | w2    | ALL    | NULL          | NULL    | NULL    | NULL             | 7074 | Using where; Not exists; Using join buffer (incremental, BNL join)     |
+------+-------------+-------+--------+---------------+---------+---------+-----------------    -+------+--------------------------------------------------------------------+

people表中的索引:id, fname, lname, license_no 养生桌:wellness_id 单位表:id, number

【问题讨论】:

  • 你能在这个查询上运行 EXPLAIN 吗?
  • Sam D:请查看我的编辑以获取您要求的信息。
  • 在可能的键下,每个字段为 NULL 表示未使用索引。因此您可以将索引添加到您用于连接的字段。例如units.residents。你可以对 wellness.people_id 做同样的事情
  • 真正做到了这一点的人,现在查询在 0.26 秒内运行......非常感谢!添加该评论作为答案,我会接受!
  • 刚刚添加。谢谢

标签: php mysql sql performance


【解决方案1】:

在可能的键下,每个具有 NULL 的字段表示未使用索引。因此您可以将索引添加到您用于连接的字段。例如units.residents。你可以对 wellness.people_id 做同样的事情——

【讨论】:

    【解决方案2】:

    此结果的连接数过多。看起来你在那里有“人”表的唯一原因是附加到“单位”,但你在“单位”和“健康”中都有可以加入的标志。我也不认为第二次加入健康表是必要的。

    这就是你所需要的:

    Select W.people_id, w.time_checked, w.username, w.return_date FROM units u left join wellness w on u.resident = w.people_id where w.time_checked < (now() - interval 48 hour) order by u.number

    您可能还想查看在不是主键的表中索引 people_id 字段。这将有助于加快您尝试运行这些表的任何查询。

    【讨论】:

    • 谢谢,但是您的查询给了我 6208 个结果,我期望的结果是 2。不过,将 Sam D. 在他的回答/评论中提到的字段编入索引已为我修复了!感谢您的帮助!
    • 第二次加入wellness 似乎用于解决“每组最大N”问题(它有助于确定wellness 行,最大time_checkedpeople_id)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-11
    • 2011-07-30
    • 1970-01-01
    • 2011-07-06
    • 1970-01-01
    相关资源
    最近更新 更多