让包含事件的表格包含以下列:
现在,让我们定义事件类型2和事件类型1.
问题是找到类型2的所有事件:
并返回此类事件的数据字段,以及验证行的数据字段(这很重要)。
真正的问题是在快速查询中执行此操作,因为事件类型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秒来执行,这已经很重要了)
答案 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.UserId
或b.UserId
为空时,条件的这部分:c.UserId
将评估为false(严格来说 - 对NULL,等于false)。