在MySQL 5.1.34服务器上,我有以下令人困惑的情况:
mysql> explain select * FROM master.ObjectValue WHERE id IN ( SELECT id FROM backup.ObjectValue ) AND timestamp < '2008-04-26 11:21:59';
+----+--------------------+-------------+-----------------+-------------------------------------------------------------+------------------------------------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------------+-----------------+-------------------------------------------------------------+------------------------------------+---------+------+--------+-------------+
| 1 | PRIMARY | ObjectValue | range | IX_ObjectValue_Timestamp,IX_ObjectValue_Timestamp_EventName | IX_ObjectValue_Timestamp_EventName | 9 | NULL | 541944 | Using where |
| 2 | DEPENDENT SUBQUERY | ObjectValue | unique_subquery | PRIMARY | PRIMARY | 4 | func | 1 | Using index |
+----+--------------------+-------------+-----------------+-------------------------------------------------------------+------------------------------------+---------+------+--------+-------------+
2 rows in set (0.00 sec)
mysql> select * FROM master.ObjectValue WHERE id IN ( SELECT id FROM backup.ObjectValue ) AND timestamp < '2008-04-26 11:21:59';
Empty set (2 min 48.79 sec)
mysql> select count(*) FROM master.ObjectValue;
+----------+
| count(*) |
+----------+
| 35928440 |
+----------+
1 row in set (2 min 18.96 sec)
更新:
花了很长时间的实际查询是DELETE,但你无法解释这些问题。删除是我使用subselect的原因。我现在已经阅读了文档并了解了语法&#34; DELETE FROM t USING ...&#34;从以下位置重写查询:
DELETE FROM master.ObjectValue
WHERE timestamp < '2008-06-26 11:21:59'
AND id IN ( SELECT id FROM backup.ObjectValue ) ;
成:
DELETE FROM m
USING master.ObjectValue m INNER JOIN backup.ObjectValue b ON m.id = b.id
WHERE m.timestamp < '2008-04-26 11:21:59';
将空备份.ObjectValue的时间从几分钟减少到.01秒。
谢谢大家的好建议。
答案 0 :(得分:5)
依赖子查询会将外部查询减慢到爬行速度(我想你知道这意味着它在被查看的数据集中每行搜索一次就会运行一次)。
您不需要子查询,不使用子查询会极大地加快查询速度:
SELECT m.*
FROM master.ObjectValue m
JOIN backup.ObjectValue USING (id)
WHERE m.timestamp < '2008-06-26 11:21:59'
MySQL经常将子查询视为依赖,即使它们不是。我从来没有真正理解其确切原因 - 可能只是因为查询优化器无法将其识别为独立。我从不打扰细节,因为在这些情况下,您几乎总是可以将其移动到FROM
子句,从而修复它。
例如:
DELETE FROM m WHERE m.rid IN (SELECT id FROM r WHERE r.xid = 10)
// vs
DELETE m FROM m WHERE m.rid IN (SELECT id FROM r WHERE r.xid = 10)
前者会产生一个从属子查询,并且可能非常慢。后者将告诉优化器隔离子查询,这样可以避免表扫描并使查询运行得更快。
答案 1 :(得分:3)
如果只需要2分钟访问所有记录,如何检查500000条记录需要3分钟?
COUNT(*)
始终在MySQL中转换为COUNT(1)
。所以它甚至不必输入每个记录,而且,我想它会使用内存中的索引来加快速度。在长时间运行的查询中,您使用范围(<
)和IN
运算符,因此对于它访问的每个记录,它必须执行额外的工作,尤其是因为它将子查询识别为依赖。 / p>
如何对单独数据库上的子查询进行分类依赖?
嗯,如果它在一个单独的数据库中并不重要。如果子查询依赖于来自外部查询的值,那么子查询是依赖的,在你的情况下你仍然可以做...但你没有,所以确实奇怪的是它被归类为依赖子查询。也许它只是MySQL中的一个错误,这就是它花了这么长时间的原因 - 它为外部查询选择的每条记录执行内部查询。
我该怎么做才能加快这个问题?
首先,请尝试使用JOIN
:
SELECT master.*
FROM master.ObjectValue master
JOIN backup.ObjectValue backup
ON master.id = backup.id
AND master.timestamp < '2008-04-26 11:21:59';
答案 2 :(得分:3)
请注意它如何说子查询只有一行?显然有超过1行。这表明mysql一次只加载一行。 mysql可能尝试做的是“优化”子查询,以便它只加载子查询中的记录,这些记录也存在于主查询(依赖子查询)中。这就是连接的工作方式,但是您对查询的表达方式已经强制推翻了连接的优化逻辑。
你告诉mysql加载备份表(子查询)然后将它与主表“timestamp&lt;'2008-04-26 11:21:59'”的过滤结果相匹配。 Mysql确定加载整个备份表可能不是一个好主意。所以mysql决定使用master的过滤结果来过滤备份查询,但是当尝试过滤子查询时,主查询还没有完成。所以它需要检查,因为它从主查询加载每个记录。因此你的从属子查询。
正如其他人提到的,使用连接,这是正确的方法。加入人群。
答案 3 :(得分:0)
真正的答案是,不要使用MySQL,它的优化器是垃圾。切换到Postgres,从长远来看,它将为您节省时间。
对于所有人都说“使用加入”,这只是一个由MySQL人群延续的废话,他们拒绝了10年来修复这个明显可怕的错误。