通过介绍......
我遇到了这个问题: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
列中的每个日期都有效,其含义为:无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)。
这就像魅力......直到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 秒。
是否还有其他方法可以优化此查询或数据结构,以便更快地执行此任务?
答案 0 :(得分:5)
即使对于中等大小的桌子,您的方法也非常慢,这并不奇怪。
理论上应该可以使用LAG
分析函数计算O(n)时间的结果,遗憾的是MySQL不支持。但是,您可以使用变量模拟MySQL中的LAG
:
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
这应该比你想要做的快几个数量级。