在Select和where子句中优化MYSQL查询TIMEDIFF

时间:2015-09-27 04:28:12

标签: mysql query-optimization alias

我有以下查询在大型数据库上运行。

select TIME_TO_SEC(TIMEDIFF(walkStartTime,walkEndTime)) 
from users 
where categoryType='1' 
and TIME_TO_SEC(TIMEDIFF(walkStartTime,walkEndTime)) < 1000

如果您注意到查询以及where子句中使用了TIME_TO_SEC。

我尝试了别名,但由于别名不能使用,所以不知道什么是更好的解决方案。

2 个答案:

答案 0 :(得分:0)

一种方法是

SELECT * FROM (
    SELECT TIME_TO_SEC(TIMEDIFF(walkStartTime,walkEndTime)) diff FROM users WHERE categoryType=1
) a
WHERE a.diff < 1000

更新

假设您有一个名为user_id的列,其中包含用户的ID

SELECT user_id, AVG(diff) as 'avg_diff', MAX(diff) as 'max_diff' FROM (
    SELECT user_id, TIME_TO_SEC(TIMEDIFF(walkStartTime,walkEndTime)) diff FROM users WHERE categoryType=1
) a
WHERE a.diff < 1000
GROUP BY 1

这将返回每个用户的id,平均差异和最大差异

答案 1 :(得分:0)

我发现只有一种优化方式来查询您的问题。当您从WHERE子句中的字段计算值时,MySQL必须计算每一行。所以每次全表扫描都会花费很多时间而且性能不高。

使用计算出的timediff和索引花费一个新字段。您不必计算它,您可以使用 VIRTUAL PERSISTENT 字段,当您插入或更改字段时,该字段会自动计算

我已经制作了相同的样本来澄清我的想法:

首先创建一个新表:

CREATE TABLE `users` (
  `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `walkStartTime` TIMESTAMP NULL DEFAULT NULL,
  `walkEndTime` TIMESTAMP NULL DEFAULT NULL,
  `categoryType` INT(11) DEFAULT NULL,
  `diffp` INT(11) AS (TIME_TO_SEC(TIMEDIFF(walkEndTime,walkStartTime))) PERSISTENT,
  PRIMARY KEY (`id`),
  KEY `diffs` (`diffp`)
) ENGINE=INNODB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

插入一些内容:

INSERT INTO `users` (`id`, `walkStartTime`, `walkEndTime`, `categoryType`)
VALUES
    (1, '2015-09-27 07:00:00', '2015-09-27 07:30:00', 1),
    (2, '2015-09-27 07:00:01', '2015-09-27 07:31:00', 1),
    (3, '2015-09-27 07:00:02', '2015-09-27 07:32:00', 0),
    (4, '2015-09-27 07:00:10', '2015-09-27 07:15:00', 1),
    (5, '2015-09-27 07:00:20', '2015-09-27 07:16:10', 1),
    (6, '2015-09-27 07:00:30', '2015-09-27 07:17:20', 0),
    (7, '2015-09-27 07:01:00', '2015-09-27 07:10:00', 1),
    (8, '2015-09-27 07:02:00', '2015-09-27 07:09:33', 1),
    (9, '2015-09-27 07:03:00', '2015-09-27 08:12:00', 1);

尝试您的查询并解析它:看到他们阅读了所有9行

MariaDB [tmp]> select TIME_TO_SEC(TIMEDIFF(walkEndTime,walkStartTime))
    -> from users
    -> where categoryType='1'
    -> and TIME_TO_SEC(TIMEDIFF(walkEndTime,walkStartTime)) < 1000;
+--------------------------------------------------+
| TIME_TO_SEC(TIMEDIFF(walkEndTime,walkStartTime)) |
+--------------------------------------------------+
|                                              890 |
|                                              950 |
|                                              540 |
|                                              453 |
+--------------------------------------------------+
4 rows in set (0.00 sec)

MariaDB [tmp]> EXPLAIN select TIME_TO_SEC(TIMEDIFF(walkEndTime,walkStartTime))
    -> from users
    -> where categoryType='1'
    -> and TIME_TO_SEC(TIMEDIFF(walkEndTime,walkStartTime)) < 1000;
+------+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+------+-------------+-------+------+---------------+------+---------+------+------+-------------+
|    1 | SIMPLE      | users | ALL  | NULL          | NULL | NULL    | NULL |    9 | Using where |
+------+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

MariaDB [tmp]>

使用新的VIRTUAL字段(diffp)运行我的优化查询:

MariaDB [tmp]> SELECT diffp
    -> FROM users
    -> WHERE diffp < 1000;
+-------+
| diffp |
+-------+
|   453 |
|   540 |
|   890 |
|   950 |
+-------+
4 rows in set (0.00 sec)

MariaDB [tmp]> EXPLAIN SELECT diffp
    -> FROM users
    -> WHERE diffp < 1000;
+------+-------------+-------+-------+---------------+-------+---------+------+------+--------------------------+
| id   | select_type | table | type  | possible_keys | key   | key_len | ref  | rows | Extra                    |
+------+-------------+-------+-------+---------------+-------+---------+------+------+--------------------------+
|    1 | SIMPLE      | users | range | diffs         | diffs | 5       | NULL |    3 | Using where; Using index |
+------+-------------+-------+-------+---------------+-------+---------+------+------+--------------------------+
1 row in set (0.00 sec)

MariaDB [tmp]>

所以你可以看到MySQL只读取3个ROWS并使用索引。 当你在两个字段(diffp和CategoryType)上使用复合索引时,也可以更快地加速,并且索引中两个字段的顺序也可以改变速度。