MySQL性能加入表本身

时间:2014-11-25 11:55:24

标签: mysql query-optimization

让包含事件的表格包含以下列:

  • eventId(整数)
  • type(整数)
  • 时间戳(unix one)
  • itemId(整数)
  • userPrimaryId(如果不存在则为NULL - 整数)
  • userSecondaryId(始终存在 - 字符串)
  • 数据(包含各种其他信息)

现在,让我们定义事件类型2和事件类型1.

问题是找到类型2的所有事件:

  • 在类型1的Event表中(让我们称之为验证事件)
  • 在时间范围内n表单类型2(即验证事件小于事件2,但不小于时间戳中n的n)
  • 事件2且验证事件必须匹配itemId和(userPrimaryId如果不是这不是null,否则在secondaryId上)

并返回此类事件的数据字段,以及验证行的数据字段(这很重要)。

真正的问题是在快速查询中执行此操作,因为事件类型2和类型1都存在数十万行。

我们在eventId(主键),类型和时间戳字段上有索引。

这就是我的立场:

  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秒来执行,这已经很重要了)

1 个答案:

答案 0 :(得分:0)

您绝对必须做的第一件事就是在不使用SELECT * FROM (subquery)的情况下重写此查询 因为到目前为止MySql并不是聪明的,并且无法将外连接谓词推入子查询。
它只是实现了两个子查询,然后加入他们的结果,这不是最佳方法。

您可以在解释此查询的计划时轻松查看,解释中将有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.UserId=c.UserIdb.UserId为空时,条件的这部分:c.UserId将评估为false(严格来说 - 对NULL,等于false)。