2014-11-25 77 views
0

要有包含事件的表,有以下欄目:MySQL的性能加入上表本身

  • 事件ID(整數)
  • 型(整數)
  • 時間戳(UNIX之一)
  • itemId(整數)
  • userPrimaryId(如果不存在,則爲NULL - 整數)
  • userSecondaryId(始終存在 - 字符串)
  • 數據(包含各種其他信息)

現在,讓我們也定義事件2型和事件型1.

的問題是找到2類型的所有事件:

  • 在類型1的事件表(我們稱之爲驗證事件)內有
  • ,它們在時間範圍內n表單類型2(即驗證事件小於事件2,但不小於n從時間戳n)
  • 事件2和驗證活動必須的itemId的驗證的數據字段匹配,並且(userPrimaryId如果沒有這個不爲空,否則就secondaryId)

並返回數據域這樣的事件,再加上行(這很重要)。

真正的問題是這樣做的快速查詢,因爲存在兩個事件類型2和類型1.

一對夫婦十萬行,我們對事件ID(主鍵)的索引,類型,和時間戳字段。

這裏就是我的立場:

SELECT 
    * 
    FROM 
    (
    SELECT 
    * 
    FROM Event 
    WHERE type=2 
    AND Time BETWEEN ${from} AND ${to} 
) b 
    INNER JOIN 
    (
    SELECT 
    * 
    FROM Event 
    WHERE type=1 
    AND Time BETWEEN (${from}-1000 AND ${to} 
) c ON b.ItemId=c.ItemId 
    AND ((b.UserId IS NOT NULL AND b.UserId=c.UserId) OR c.CookieId=b.CookieId) 

我目前的做法是在兩個單獨的查詢選擇兩個事件類型,內部加入他們的行列。

現在我的問題是如何只保留具有最大時間戳的那一行,如果我通過eventId對類型2元素進行分組。

任何優秀的解決方案,或更快的查詢執行的替代方法? (上加入大約需要100秒來執行,這已經是顯著)

+0

對於你的倒數第二段,你的意思是用'ItemId'或'eventId'分組嗎?每個'eventId'只能有一個'timestamp',所以我找不到每個'eventId'的最大時間戳。此外,'UserId'與'userPrimaryId'相同,'CookieId'與'userSecondaryId'相同?列出的列與示例查詢中的列不同。 – 2014-11-26 20:49:30

回答

0

你絕對必須做的第一件事就是重寫此查詢,而無需使用SELECT * FROM (subquery)
因爲到目前爲止,MySQL是不聰明enaugh並不能推動外將謂詞連接到子查詢中。
它只是物化兩個子查詢,然後加入他們的結果,什麼不是最佳方法。

你可以看到它很容易在解釋此查詢的計劃,將有4行的解釋,看起來或多或少是這樣的:

------- + ---------------- + 
| id  | select_type  | 
+ ------- + ---------------- + 
| 1  | PRIMARY   | 
| 1  | PRIMARY   | 
| 3  | DERIVED   | 
| 2  | DERIVED   | 
+ ------- + ---------------- + 

重寫的查詢:

SELECT * 
FROM Event b 
INNER JOIN Event c 
ON b.ItemId=c.ItemId 
    AND ((b.UserId IS NOT NULL AND b.UserId=c.UserId) OR c.CookieId=b.CookieId) 
WHERE b.type=2 
    AND b.Time BETWEEN ${from} AND ${to} 
    AND c.type=1 
    AND c.Time BETWEEN (${from}-1000 AND ${to} 

在這之後,你應該得到一個解釋是這樣的:

+ ------- + ---------------- + 
| id  | select_type  | 
+ ------- + ---------------- + 
| 1  | SIMPLE   | 
| 1  | SIMPLE   | 
+ ------- + ---------------- + 



而在最後創建此兩列的索引:

CREATE INDEX ev_type_tm ON event(type, time); 

一個備註:
在這種情況下:

AND ((b.UserId IS NOT NULL AND b.UserId=c.UserId) 

b.UserId IS NOT NULL是冗餘的,並且可以是跳過,
因爲這部分條件:當b.UserIdc.UserId爲空時,將評估爲假(嚴格來說,爲NULL,相當於假)。

+0

所以這裏就是我最終使用: SELECT * \t \t從事件B \t \t INNER JOIN事件C \t \t ON b.ItemId = c.ItemId \t \t AND(b.UserId = c.UserId OR c.CookieId = b.CookieId) \t \t WHERE b.EventType = 2 \t \t AND b.Time BETWEEN UNIX_TIMESTAMP( '2014年12月2日')* 1000和UNIX_TIMESTAMP( '2014年12月3日')* 1000 \t \t AND c.EventType = 11 \t \t AND c.Time BETWEEN b.Time AND b。時間7 * 24 * 3600 * 1000; ID \t \t SELECT_TYPE表\t \t類型possible_keys \t \t鍵key_len \t \t參考行\t額外 \t SIMPLE b \t \t範圍PRIMARY,IB_UserId_All \t \t PRIMARY 10 \t \t 使用其中;使用連接緩衝 SIMPLE \tÇ\t裁判\t PRIMARY,IB_UserId_All \t PRIMARY 常量 這是痛苦的緩慢:( – Yeti 2014-12-05 16:01:57

+0

你有沒有創建我曾在我的答案中提到的指數?請發表你的表結構,包括索引定義,解釋說明只有一個索引'IB_UserId_All'(主鍵除外)。 – krokodilko 2014-12-05 17:14:09