MySQL:在内部查询中使用“ ORDER BY”优化UNION


9

我只是建立了一个由多个具有相同布局的表组成的日志系统。

每个数据源都有一个表。

对于日志查看器,我想

  • UNION所有日志表
  • 通过帐户过滤它们
  • 添加一个伪列以标识源,
  • 按照时间排序他们
  • 限制它们的分页

所有表都包含一个称为zeitpunkt索引日期/时间列的字段。

我的第一次尝试是:

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt AS zeit,
 'hp' AS source FROM is_log AS l WHERE l.account_id = 730)

UNION

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt,
 'ig' AS source FROM ig_is_log AS l WHERE l.account_id = 730)

ORDER BY zeit DESC LIMIT 10;

优化器无法在此处使用索引,因为两个表中的所有行均由子查询返回并在后面排序UNION

我的解决方法如下:

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt AS zeit,
 'hp' AS source FROM is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10)

UNION

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt,
 'ig' AS source FROM ig_is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10)

ORDER BY zeit DESC LIMIT 10;

我期望查询引擎将在此处使用索引,因为两个子查询都应该在之前进行排序和限制UNION,然后再合并和排序行。

我真的以为是这样,但是EXPLAIN在查询上运行告诉我子查询仍在搜索两个表。

EXPLAINing子查询本身向我显示了所需的优化,但UNIONing它们却没有。

我错过了什么?

我知道子查询中的ORDER BY子句UNION不带会被忽略LIMIT,但是有一个限制。

编辑:
实际上,可能还会有没有account_id条件的查询。

这些表已经存在,并已填充数据。布局可能会有所不同,具体取决于来源,因此我想让它们保持分开。此外,由于某种原因,日志记录客户端使用不同的凭据。

我必须在日志读取器和实际表之间保留一层。

以下是整个查询和第一个子查询的执行计划以及表布局的详细信息:

https://gist.github.com/ca8fc1093cd95b1c6fc0


1
最好的指标就是化合物(account_id, zeitpunkt)。你有这样的索引吗?第二个最佳选择是(我认为)单个(zeitpunkt)-但使用效率取决于行account_id=730出现的频率。
ypercubeᵀᴹ

2
又为什么UNION DISTINCT呢?无需在此处强制排序和区分,因为由于额外的标识列,结果在各个子查询中将有所不同。使用UNION ALL
ypercubeᵀᴹ

1
除了@ypercube的建议外,我还有一个问题:将所有这些日志添加到同一表中,再加上列,会不会更好source?这样,您可以避免UNION使用s并在所有数据中使用索引。
dezso 2012年

1
@ypercube实际上,可能还会有一些没有account_id条件的查询。该DISTINCT标志是之前尝试的孑遗和实际上是无用的,因为结果总是会有所不同,因为DISTINCT是dafualt行为。这些表已经存在,并已填充数据。无论如何,版式可能会有所不同,具体取决于来源,因此我想将它们分开。此外,由于某种原因,日志记录客户端使用不同的凭据。我必须在日志读取器和实际表之间保留一层。
卢卡斯2012年

可以,但是检查是否更改为UNION ALL不同的执行计划。
ypercubeᵀᴹ

Answers:


8

出于好奇,您可以尝试此版本吗?可能会诱使优化器使用与子查询分开使用的相同索引:

SELECT *
FROM
(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt AS zeit,
 'hp' AS source FROM is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10) 
    AS a

UNION ALL

SELECT *
FROM
(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt,
 'ig' AS source FROM ig_is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10)
    AS b

ORDER BY zeit DESC LIMIT 10;

我仍然认为您可能拥有的最佳指数是复合物(account_id, zeitpunkt)。它将快速产生10行,不需要任何技巧。


您所做的修改带来了预期的结果。谢谢!顺便提一下:到目前为止,我不确定哪个索引会更好。我什至可以同时使用。我将不得不检查用户数量和log entries / user意愿如何扩展。
卢卡斯2012年

如果您需要使用和不使用的查询account_id=?,请保留两者。
ypercubeᵀᴹ

@ ypercube,+ 1非常聪明,也可以在我的(类似)情况下使用!您能否解释为什么将联合查询包装在虚拟对象中会SELECT * FROM导致MySQL使用索引吗?
dkamins 2013年

@dkamins:MySQL优化器不是很聪明,通常当有一个诸如此处的派生表时(SELECT ...) AS a,它会尝试与其他派生表分开,然后对整个查询进行评估和优化。
ypercubeᵀᴹ

@Lukas,实际上,由于您确实需要确保使用索引,因此使用/添加force index将为您提供更好的解决方案。
Pacerier,2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.