2014-11-25 76 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