2012-04-25 61 views
5

通過介紹的方式...
我遇到了這個問題: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?),其答案描述瞭如何從下一個/上一個行獲取值。它仍然依賴於一些固定值,但我學會了如何使用這種技術。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 

有兩個假設:

  1. 主鍵(id)有序上升,並允許 「洞」。
  2. 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秒。


還有沒有其他的方法來優化這個查詢或數據結構,以有該任務執行得更快?

+0

我認爲不同的數據結構可能更適合您的需求。你可以多說一點你如何使用這些數據? – eggyal 2012-04-25 18:03:07

+0

@eggyal沒什麼特別的。我只是想學習一些可能有用的東西:) – bostaf 2012-04-25 18:30:00

回答

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 

這應該是幾個數量級比你正在嘗試做的更快。

+0

他可能也從CROSS APPLY類型語句中受益,但是這在MySQL中可惜並不可用。提醒我爲什麼MySQL如此受歡迎? – 2012-04-25 21:10:05

+3

好的答案,順便說一句。這裏的小提琴鏈接顯示它的工作 - http://sqlfiddle.com/#!2/099fc/5 – 2012-04-25 21:11:27

+1

這個訣竅是輝煌的。查詢以我的示例數據集立即執行,大約1秒鐘的數據量爲200萬行。感謝您的解決方案,特別是對於這項技術 - 它確實很有用。 – bostaf 2012-04-26 05:55:01