Taz*_*Taz 5 mysql iteration optimization datediff query-optimization
通过介绍...
我遇到了这个问题: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;
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
有两个假设:
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
Run Code Online (Sandbox Code Playgroud)
凭借我所学到的一切,我来到了这个解决方案(说解决方案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;
Run Code Online (Sandbox Code Playgroud)
(例如这里的小提琴: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
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
...并获得了微小的改进:
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
Run Code Online (Sandbox Code Playgroud)
18031行:持续时间:8.406秒.取:219.281秒.
在某些情况下,我回想起在某处了解MyISAM优于InnoDB的优势.所以我改为MyISAM:
ALTER TABLE `foo` ENGINE = MyISAM;
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
......但它甚至更慢:
18031行:持续时间:15.609秒.获取:184.656秒.
有没有其他方法来优化此查询或数据结构,以便更快地执行此任务?
即使对于中等大小的桌子,您的方法也很慢也就不足为奇了.
从理论上讲,使用LAG分析函数可以在O(n)时间内计算结果,遗憾的是MySQL不支持.但是,您可以LAG使用变量在MySQL中进行模拟:
SELECT
id,
DATE_FORMAT(f.dateof, '%b %e, %Y') AS date,
DATEDIFF(dateof, @prev) AS days_diff,
@prev := dateof
FROM FOO, (SELECT @prev := NULL) AS vars
ORDER BY id
Run Code Online (Sandbox Code Playgroud)
这应该比您尝试的速度快几个数量级.