導入として...
私はこの質問に出くわしました: 2 つの隣接するフィールドの違い - 日付 - PHP MYSQLで、目標を達成しようとしていました。
もう 1 つの質問 ( Subtracting one row of data from another in SQL ) は、MySQL で同様のものを作成する方法を理解するのに役立ちました。解決策はまだ固定値またはデータの想定順序に依存しているため、問題は解決しませんでしたが、方法論を理解するのに役立ちました。
もう 1 つ質問があります ( 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
次の 2 つの仮定があります。
- 主キー (
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にエントリが2つだけになるまで。次の場合はさらに悪化します。
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秒。
場合によっては、InnoDB に対する MyISAM の利点についてどこかで読んだことを思い出しました。だから私は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秒。
このタスクをより速く実行するために、このクエリまたはデータ構造を最適化する他の方法はありますか?