作为介绍...
我遇到了这个问题:2 个相邻字段之间的差异 - 日期 - PHP MYSQL并试图实现目标,即使用纯 MySQL 遍历日期并获得差异。
那里的另一个问题(在 SQL 中从另一行中减去一行数据)帮助我了解如何使用 MySQL 制作类似的东西。它没有解决问题,因为解决方案仍然依赖于固定值或假定的数据顺序,但它确实帮助我理解了该方法。
还有另一个问题(如何在 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
都是有效的,意思是:没有NULL
s 和没有默认值 (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 )。
这就像一个魅力......直到数据库中只有几个条目。当更多时它变得更糟:
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秒。
是否有任何其他方法可以优化此查询或数据结构以便更快地执行此任务?