【问题标题】:Optimize SQL-Query several JOINs优化 SQL-Query 几个 JOIN
【发布时间】:2012-07-02 08:54:04
【问题描述】:

我有一个带有嵌套连接的 SQL 查询:

SELECT rh.host, rh.report, COUNT(results.id), COUNT(results_2.id), COUNT(results_3.id), COUNT(results_4.id)
FROM report_hosts rh
INNER JOIN report_results rr ON rh.report = rr.report
LEFT OUTER JOIN results ON rr.result = results.id AND results.type =  'Hole' AND results.host = rh.host
LEFT OUTER JOIN results results_2 ON rr.result = results_2.id AND results_2.type =  'Warning' AND results_2.host = rh.host
LEFT OUTER JOIN results results_3 ON rr.result = results_3.id AND results_3.type =  'Note' AND results_3.host = rh.host
LEFT OUTER JOIN results results_4 ON rr.result = results_4.id AND results_4.type =  'Log' AND results_4.host = rh.host
GROUP BY rh.host

查询原样需要大约 5 秒,其中 99.7%复制到临时表。完整查询的EXPLAIN 显示为:

+----+-------------+-----------+--------+---------------+---------+---------+-------------------+------+---------------------------------+
| id | select_type | table     | type   | possible_keys | key     | key_len | ref               | rows | Extra                           |
+----+-------------+-----------+--------+---------------+---------+---------+-------------------+------+---------------------------------+
|  1 | SIMPLE      | rr        | ALL    | report        | NULL    | NULL    | NULL              | 3139 | Using temporary; Using filesort |
|  1 | SIMPLE      | rh        | ref    | report        | report  | 5       | openvas.rr.report |  167 | Using where                     |
|  1 | SIMPLE      | results   | eq_ref | PRIMARY,type  | PRIMARY | 4       | openvas.rr.result |    1 |                                 |
|  1 | SIMPLE      | results_2 | eq_ref | PRIMARY,type  | PRIMARY | 4       | openvas.rr.result |    1 |                                 |
|  1 | SIMPLE      | results_3 | eq_ref | PRIMARY,type  | PRIMARY | 4       | openvas.rr.result |    1 |                                 |
|  1 | SIMPLE      | results_4 | eq_ref | PRIMARY,type  | PRIMARY | 4       | openvas.rr.result |    1 |                                 |
+----+-------------+-----------+--------+---------------+---------+---------+-------------------+------+---------------------------------+

当我删除LEFT JOINs 时,查询会在大约 1 秒内执行,每个LEFT JOIN 会增加大约一秒的执行时间。

我的问题: 谁能解释一下,如果有更多LEFT JOINs,为什么一个连接的复制到临时表任务需要更长的时间? MySQL 是否会为每个 JOIN 多次复制临时表?

我怎样才能避免这种情况?我是否缺少索引?

我打算完成的工作: 我有一张表格,上面有几台主机的扫描结果。每个结果都按类型分类(“孔”、“警告”、“注释”或“日志”)。我想选择每个主机和相应数量的漏洞、警告、注释和日志。作为“限制”,我有一个事实,即并非每个主机都有每种类型的结果。

【问题讨论】:

  • 如果 results1 和 results2 都有 10 条匹配记录,您现在有 100 条记录。每个左连接都会进一步增加。这不是您要查找的查询。 [我会发布更多,但我在打电话]

标签: mysql sql query-optimization


【解决方案1】:

您多次加入一个表,这实际上就像加入多个表一样。您应该能够使用一些 case 语句和 where 子句来处理它。 (实际上你可能不需要 where 子句。)

SELECT rh.host, rh.report, 
 COUNT(CASE WHEN results.type = 'Hole' THEN 1 ELSE NULL END) as Holes, 
 COUNT(CASE WHEN results.type = 'Warning' THEN 1 ELSE NULL END) as Warnings,
 COUNT(CASE WHEN results.type = 'Note' THEN 1 ELSE NULL END) as Notes, 
 COUNT(CASE WHEN results.type = 'Log' THEN 1 ELSE NULL END) as Logs
FROM 
 report_hosts rh
INNER JOIN 
 report_results rr 
ON 
 rh.report = rr.report
LEFT OUTER JOIN 
 results 
ON 
 rr.result = results.id 
 AND results.host = rh.host
WHERE
 results.type = 'Hole' 
 OR results.type = 'Warning' 
 OR results.type = 'Note' 
 OR results.type = 'Log'
GROUP BY rh.host, rh.report

案例语句(IME)并不是表现最好的,但来自许多连接的数据膨胀可能会抵消这一点并提供更好的性能。

【讨论】:

  • 避免多重连接的好主意!查询仅需 1 秒。但是,一件奇怪的事情:如果我省略了WHERE 子句,执行时间会超过 10 秒。 results 表有 6000 行,但只有 600 行匹配WHERE 子句。
  • 如果results.type 是一个索引,他们正在计算它们在使用WHERE 而不使用WHERE 时的索引值,它正在执行全表扫描。
  • 所以原因是,只有WHERE 子句可以“请求”索引,而不能“请求”聚合函数?
【解决方案2】:

使用大量数据(在您的情况下是额外的left join)意味着将其存储在内存中。

如果您耗尽缓冲区,您的查询将需要存储到驱动器上的临时结果表中。

尝试使用相同数量的left joins,但使用limit 限制行数。它应该确认问题出在缓冲区中(意味着它会运行得更快)。

【讨论】:

  • 我知道,问题出在LEFT JOINs。使用INNER JOINs,速度要快得多,但我想我的查询不适用于内部连接,是吗?
  • 内连接主要处理较少的数据,这就是直接比较速度更快的原因。
  • 可行 取决于您的需求。 Left joins 获取左侧表中的所有值以及右侧表中的任何可用值(否则为空)。 Inner joins 只获取左侧对应右侧的值。正如 YvesR 所说,这意味着更少的价值。我不知道您的表格用于什么或其中的数据是什么,所以我无法告诉您选择更多或更少的数据是否可以。
  • 我已经添加了我想通过查询完成的目标。也许 SQL 不是实现这一点的最佳方式,我应该在我的编程语言中做“重”的部分。
  • 如果@Ilion 提出的查询有效并产生您期望的结果,我会推荐它。
猜你喜欢
  • 1970-01-01
  • 2016-01-05
  • 2012-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-05
  • 2015-10-26
相关资源
最近更新 更多