【发布时间】:2012-05-06 11:02:56
【问题描述】:
作为介绍...
我遇到了这个问题:Difference between 2 adjacent fields - Date - PHP MYSQL 并试图实现目标,即使用纯 MySQL 遍历日期并获得差异。
那里的另一个问题 (Subtracting one row of data from another in SQL) 帮助我了解如何使用 MySQL 制作类似的东西。它没有解决问题,因为解决方案仍然依赖于固定值或假定的数据顺序,但它确实帮助我理解了方法。
还有另一个问题 (How to get next/previous record in MySQL?) 的答案描述了如何从下一行/上一行获取值。它仍然依赖于一些固定值,但我学会了如何使用该技术。
假设我有这张桌子foo:
CREATE TABLE `foo` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`dateof` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
id | dateof
-----+------------
1 | 2012-01-01
2 | 2012-01-02
11 | 2012-01-04
12 | 2012-01-01
13 | 2012-01-02
14 | 2012-01-09
111 | 2012-01-01
112 | 2012-01-01
113 | 2012-01-01
有两个假设:
- 主键 (
id) 按升序排列,允许“空洞”。 -
dateof列中的每个日期都是有效的,意思是:没有NULLs 并且没有默认值 (0000-00-00)。 我想遍历每一行并计算上一个条目经过的天数,以接收:
id | date | days_diff
-----+------------+-----------
1 | 2012-01-01 | 0
2 | 2012-01-02 | 1
11 | 2012-01-04 | 2
12 | 2012-01-01 | -3
13 | 2012-01-02 | 1
14 | 2012-01-09 | 7
111 | 2012-01-01 | -8
112 | 2012-01-01 | 0
113 | 2012-01-01 | 30
根据我所学到的所有知识,我来到了这个解决方案(比如说解决方案 1,因为还有另一个):
SELECT
f.id,
DATE_FORMAT(f.dateof, '%b %e, %Y') AS date,
(SELECT DATEDIFF(f.dateof, f2.dateof)
FROM foo f2
WHERE f2.id = (
SELECT MAX(f3.id) FROM foo f3 WHERE f3.id < f.id
)
) AS days_diff
FROM foo f;
(此处的小提琴示例:http://sqlfiddle.com/#!2/099fc/3)。
这就像一个魅力......直到 db 中只有几个条目。越多情况就越糟:
EXPLAIN:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY f ALL NULL NULL NULL NULL 17221
2 DEPENDENT SUBQUERY f2 eq_ref PRIMARY PRIMARY 4 func 1 Using where
3 DEPENDENT SUBQUERY f3 index PRIMARY PRIMARY 4 NULL 17221 Using where; Using index
18031 行:持续时间:8.672 秒。获取:228.515 秒。
我想在dateof 列上添加索引:
CREATE TABLE `foo` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`dateof` date DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `dateof` (`dateof`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
...并获得了微小的改进:
EXPLAIN:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY f index NULL dateof 4 NULL 18369 Using index
2 DEPENDENT SUBQUERY f2 eq_ref PRIMARY PRIMARY 4 func 1 Using where
3 DEPENDENT SUBQUERY f3 index PRIMARY dateof 4 NULL 18369 Using where; Using index
18031 行:持续时间:8.406 秒。获取:219.281 秒。
我记得在某处读过有关 MyISAM 在某些情况下优于 InnoDB 的优势。所以我把它改成了 MyISAM:
ALTER TABLE `foo` ENGINE = MyISAM;
18031 行:持续时间:5.671 秒。获取:151.610 秒。
当然更好,但仍然很慢。
我尝试了另一种算法(解决方案 2):
SELECT
f.id,
DATE_FORMAT(f.dateof, '%b %e, %Y') AS date,
(SELECT DATEDIFF(f.dateof, f2.dateof)
FROM foo f2
WHERE f2.id < f.id
ORDER BY f2.id DESC
LIMIT 1
) AS days_diff
FROM foo f;
...但它更慢:
18031 行:持续时间:15.609 秒。获取:184.656 秒。
是否有任何其他方法可以优化此查询或数据结构以更快地执行此任务?
【问题讨论】:
-
我认为不同的数据结构可能更适合您的需求。你能多说一下你是如何使用这些数据的吗?
-
@eggyal 没什么特别的。我只是想学习一些可能有用的东西:)
标签: mysql optimization query-optimization iteration datediff