2014-02-27 19 views
1

注意:我發現this similar question但它沒有解決我的問題,所以我不認爲這是重複的。我有兩個簡單的MySQL表(使用MyISAM引擎創建),Table1Table2。我有兩個簡單的MySQL表(使用MyISAM引擎創建),Table1Table2MySQL加入日期列與1個月的滯後和性能問題

這兩個表都有3列,日期類型列,整數ID列和浮點值列。這兩張表格都有大約300萬條記錄,而且非常簡單。

的表的內容如下所示(與DateId作爲主鍵):

Date  Id Var1 
2012-1-27 1 0.1 
2012-1-27 2 0.5 
2012-2-28 1 0.6 
2012-2-28 2 0.7 

(假設Var1變得Var2用於第二表)。

請注意,對於每個(年,月,ID)三元組,只會有一個條目。但是,出現月份的實際一天並不一定是最後一天,也不是最後一個工作日,也不是最後一個工作日,等等。這只是一個月中的某一天。這一天作爲其他表格中的觀察日很重要,但在Table1Table2之間的月份本身並不重要。

因此,我不能依靠Date + INTERVAL 1 MONTH生成匹配的日期,以便匹配日期與未來的一個月。

我期待加入這兩個表上DateId但如果從第二個表(Var2)的值來自1個月的領先比Var1

這種代碼將完成它,但我注意到這一點顯着的性能下降,下面解釋。

-- This is exceptionally slow for me 
SELECT b.Date, 
     b.Id, 
     a.Var1, 
     b.Var2 
FROM Table1 a 
JOIN Table2 b 
ON a.Id = b.Id 
AND YEAR(a.Date + INTERVAL 1 MONTH) = YEAR(b.Date) 
AND MONTH(a.Date + INTERVAL 1 MONTH) = MONTH(b.Date) 


-- This returns quickly, but if I use it as a sub-query 
-- then the parent query is very slow. 
SELECT Date + INTERVAL 1 MONTH as FutureDate, 
     Id, 
     Var1 
FROM Table1 


-- That is, the above is fast, but this is super slow: 
select b.Date, 
     b.Id, 
     a.Var1, 
     b.Var2 
FROM (SELECT Date + INTERVAL 1 MONTH as FutureDate 
      Id, 
      Var1 
     FROM Table1) a 
JOIN Table2 b 
ON YEAR(a.FutureDate) = YEAR(b.Date) 
AND MONTH(a.FutureDate) = MONTH(b.Date) 
AND a.Id = b.Id 

我試圖重新排序JOIN標準,想也許在Id第一代碼會改變查詢執行計劃是匹配的,但它似乎沒有什麼區別。

當我說「超級慢」時,我的意思是,即使我等了一個多小時,上述代碼中的選項#1也不會返回所有300萬條記錄的結果。選項#2在不到10分鐘內返回,但選項3再次超過1小時。

我不明白爲什麼引入日期滯後使得花費這麼長時間。

  1. 如何可以分析查詢以瞭解爲什麼它需要很長的時間?
  2. 基於1個月的日期滯後(其中由1個月的滯後導致的月份日可能導致不匹配)爲聯接表編寫更好的查詢。
+0

如何創建和索引'dateYear'和'dateMonth'列(作爲整數)?我相信什麼會影響你的表現是使用'YEAR'和'MONTH'功能。 –

+0

這可以工作,但理想情況下,我不想在任何地方傳播它。實際上有數百個像Table1和Table2這樣的表,需要進行這個過程。我想我可以嘗試一個子查詢,它返回一個等於'FutureDate'的'100 * Year + Month'的列(在我的第二個示例中),並查看該子查詢在父查詢中是否更快,因爲連接只會在整數列上。 – ely

+0

您是否找到解決方案?使用YEAR()和MONTH()條件時,我遇到了緩慢連接的類似問題。 –

回答

2

這是一種替代方法:

SELECT b.Date, b.Id, b.Var2 
     (select a.var1 
     from Table1 a 
     where a.id = b.id and a.date < b.date 
     order by a.date 
     limit 1 
     ) as var1 
     b.Var2 
FROM Table2 b; 

確保主索引設置了id第一,然後在Table1date。否則,創建另一個索引Table1(id, date)

注意,這個假設前面的日期的前一個月。

+0

需要'Id'作爲主索引的第一部分,以便每個'Id'值(和排序)上的重複子查詢將足夠快? – ely

+0

@EMS。 。 。是的,那是原因。您希望相關的子查詢將該索引用於大部分工作。 –

1

這裏的去了解這個另一種替代方式:

SELECT thismonth.Date, 
     thismonth.Id, 
     thismonth.Var1 AS Var1_thismonth, 
     lastmonth.Var1 AS Var1_lastmonth 
    FROM Table2 AS thismonth 
    JOIN 
     (SELECT id, Var1, 
       DATE(DATE_FORMAT(Date,'%Y-%m-01')) as MonthStart 
     FROM Table2 
    ) AS lastmonth 
    ON ( thismonth.id = lastmonth.id 
     AND thismonth.Date >= lastmonth.MonthStart + INTERVAL 1 MONTH 
     AND thismonth.Date < lastmonth.MonthStart + INTERVAL 2 MONTH 
     ) 

爲了得到這個理想地進行,我想你會需要對(ID,日期,VAR1)覆蓋指數的化合物。

它通過產生含有Id,MonthStart,Var1派生表,然後通過範圍掃描的序列加入原始表給它。因此化合物覆蓋指數。

+0

lastmonth.MonthStart + INTERVAL 1 MONTH是否大於'thismonth.Date'? 'MonthStart'只是將它移回到同一個月的「01」日。爲此添加1個月的INTERVAL應該給出下個月的「01」日,該日仍然大於「thismonth.Date」,否? – ely

+0

那麼,lastmonth.MonthStart是上個月的第一天。要將它匹配到日期在當前月份開始或之後的行,可以使用'thismonth.Date> = lastmonth.MonthStart + INTERVAL 1 MONTH'。同樣,要將它匹配到日期在下個月開始之前的行,則使用'thismonth.Date

+0

順便說一句,如果您需要結果集的行數爲上個月沒有觀察的月份,請將LEFT JOIN替換爲JOIN。這些情況下你會得到NULL值。戈登林諾夫的答案也是如此。 –

0

其他的答案給了非常有用的技巧,但最終,未做顯著修改我的數據的索引結構(這是不可行的時刻),這些方法是行不通的更快(在任何意義上)比我已經嘗試過這個問題。

奧利·瓊斯給我的主意,使用日期格式,以及與TIMESTAMPDIFF功能似乎讓它還算過得去剛性聯軸器,但我還是歡迎解釋任何意見,爲什麼使用YEARMONTHDATE_FORMATTIMESTAMPDIFF有這樣的性能差異很大。

SELECT b.Date, 
     b.Id, 
     b.Var2, 
     a.Date, 
     a.Id, 
     a.Var1 
FROM Table1 a 
JOIN Table2 b 
ON a.Id = b.Id 
AND (TIMESTAMPDIFF(MONTH, 
        DATE_FORMAT(a.Date, '%Y-%m-01'), 
        DATE_FORMAT(b.Date, '%Y-%m-01')) = 1)