【问题标题】:TIMEDIFF query taking a long time even with indexes on columns即使在列上有索引,TIMEDIFF 查询也需要很长时间
【发布时间】:2017-05-20 12:14:08
【问题描述】:

我有一个名为lead_history 的表,当我从中计算平均值时,查询需要一分半钟。该查询正在解析大约 15k 行。我已经根据以下查询添加了索引,但似乎仍然需要很长时间。任何帮助将不胜感激。

 SELECT AVG(TIMEDIFF(tq.ts, tv.ts)) / 60 avg_time_to_quote 
   FROM lead_history tv 
   JOIN lead_history tq 
     ON tv.agency_id = tq.agency_id  
  WHERE tv.new_status = 'Verified' 
    AND tq.new_status = 'Quoted' 
    AND tv.agency_id = '$agency_id' 
    AND tv.ts > DATE_SUB(NOW(), INTERVAL 30 DAY) 
    AND tq.ts > DATE_SUB(NOW(), INTERVAL 30 DAY) 
  GROUP 
     BY tv.agency_id
      , tq.agency_id

表结构

show create table lead_history;
+--------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table        | Create Table                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
+--------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| lead_history | CREATE TABLE `lead_history` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `agency_id` varchar(255) NOT NULL,
  `old_status` varchar(64) DEFAULT NULL,
  `new_status` varchar(64) DEFAULT NULL,
  `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `contact_id` varchar(255) NOT NULL DEFAULT '',
  `alter_type` varchar(255) NOT NULL DEFAULT '',
  `last_mod_by` varchar(64) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `avg_index` (`old_status`,`new_status`,`agency_id`,`ts`)
) ENGINE=InnoDB AUTO_INCREMENT=14041 DEFAULT CHARSET=latin1 |
+--------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

索引

mysql> show indexes from lead_history;

+--------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table        | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| lead_history |          0 | PRIMARY   |            1 | id          | A         |       13922 |     NULL | NULL   |      | BTREE      |         |               |
| lead_history |          1 | avg_index |            1 | old_status  | A         |          18 |     NULL | NULL   | YES  | BTREE      |         |               |
| lead_history |          1 | avg_index |            2 | new_status  | A         |          46 |     NULL | NULL   | YES  | BTREE      |         |               |
| lead_history |          1 | avg_index |            3 | agency_id   | A         |          48 |     NULL | NULL   |      | BTREE      |         |               |
| lead_history |          1 | avg_index |            4 | ts          | A         |        1330 |     NULL | NULL   |      | BTREE      |         |               |
+--------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+`

解释`

mysql> explain select avg(UNIX_TIMESTAMP(tq.ts) - UNIX_TIMESTAMP(tv.ts)) / 60 as avg_time_to_quote from lead_history tv join lead_history tq on tv.agency_id = tq.agency_id  WHERE tv.old_status not like 'Verified' and tq.new_status = 'Verified' and tv.agency_id and tv.agency_id = 'XXXXXXXXXXXX35';
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+-------+----------+-----------------------------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key       | key_len | ref  | rows  | filtered | Extra                                                           |
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+-------+----------+-----------------------------------------------------------------+
|  1 | SIMPLE      | tq    | NULL       | index | NULL          | avg_index | 395     | NULL | 13922 |     1.00 | Using where; Using index                                        |
|  1 | SIMPLE      | tv    | NULL       | index | NULL          | avg_index | 395     | NULL | 13922 |     8.00 | Using where; Using index; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+-------+----------+-----------------------------------------------------------------+
2 rows in set, 1 warning (0.02 sec)

mysql>

`

【问题讨论】:

  • 查看这个问题的答案:stackoverflow.com/questions/3528219/… 它说减去 UNIX_TIMESTAMP(a)-UNIX_TIMESTAMP(b) 可以比在 TIMESTAMP 字段上使用 TIMEDIFF 更快。
  • 哇!我不敢相信这有多大的不同。将查询缩短一分钟。不过,30 秒似乎仍然很长。还有其他想法吗?
  • 您有带间隔的 DATE_SUB() 函数。从 UNIX_TIMESTAMP(TIMESTAMP) 中减去一个整数 = 30 天 * 24 小时 * 3600 秒怎么样?
  • 顺便说一句,我觉得 SHOW CREATE TABLE 比 DESCRIBE 更容易阅读
  • @Strawberry 那是显示创建表。 注意 - 我明白你的意思,那绝对不是 show create table,抱歉混淆了,改了。

标签: php mysql indexing average


【解决方案1】:
  • 你的索引因为顺序没用——加INDEX(agency, new_status, ts)

  • GROUP BY... 可能是个好主意,但这个答案并不追求那个。

  • 将“状态”列从大容量 VARCHAR 更改为 1 字节 ENUM

考虑一种不同的方法:两张表——一张带有审计跟踪(或交易历史);一个具有当前状态的。有了这个,就不需要JOIN 来做差异。另一方面,它需要任何一个

  • INSERT 进入历史表加上UPDATE 状态表;或
  • 历史表上的TRIGGER 以更新状态表。

【讨论】:

    猜你喜欢
    • 2017-02-18
    • 2019-10-27
    • 1970-01-01
    • 2019-03-09
    • 2020-05-22
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    相关资源
    最近更新 更多