2009-10-09 51 views
2

如果我运行以下查询,每个人都会快速返回(0.01秒),并为我提供所需的结果。使用IN谓词的MySQL性能

SELECT tagId FROM tag WHERE name='programming'

SELECT COUNT(DISTINCT workcode) FROM worktag WHERE tagId=123 OR tagId=124

(假设这两个标签识别数字是从第一个查询的结果)

我想,所以我只需要一次运行这些查询结合:

SELECT COUNT(DISTINCT workcode) FROM worktag WHERE tagId IN (SELECT tagId FROM tag WHERE name='programming')

然而,这个查询在大约1分钟和20分钟内完成秒。我有索引worktag.workcode,worktag.tagId,tag.tagIdtag.name

如果我在查询上运行DESCRIBE,前两个使用索引,第二个使用子查询的索引(在tag表上),但不使用worktag表上的任何索引。

有谁知道这可能是为什么?

注意:worktag表中有超过1800万条记录。

+0

仅供参考:MySQL中的IN vs范围:http://explainextended.com/2009/10/07/in-list-vs-range-condition-mysql/ – 2009-10-09 21:03:15

回答

2

为什么不使用连接而不是子查询?

SELECT COUNT(DISTINCT workcode) 
FROM worktag 
LEFT JOIN tag 
    ON worktag.tagId = tag.tagID 
WHERE tag.name = 'programming' 

P.S .:似乎是reported as bug

+0

我的同事刚过来给我完全一样的解。据他介绍,mysql可以更容易地将JOIN优化为子查询。我不太喜欢这种说法(子查询对我来说很好看),但它效果很好。谢谢。 – chadgh 2009-10-09 20:30:10

+0

子查询更为重要:您明确要求先选择ID,然后根据以前的结果选择计数。在我看来,这给予优化者更少的自由。 (尽管你的问题似乎仍然是一个错误,而不是EBKAC!:) – Zed 2009-10-09 20:35:51

0

你试过:

SELECT COUNT(DISTINCT workcode) FROM worktag WHERE tagId IN (123, 124) 

我不是MySQL专家,但是在我看来,您可能正在查看查询优化器的重大故障。

另一方面,对MySQL有利,它在第二个语句中优化了OR。我知道将成功优化IN()的数据库,但不知道相同逻辑请求的OR版本。

1

最近数据库管理员告诉我,语法WHERE x IN (...)是数据库的痛处。一个连接是几乎总是更好:

SELECT COUNT(DISTINCT wt.workcode) 
    FROM worktag wt, tag t 
WHERE wt.tagId = t.tagId 
    AND t.name='programming' 
+0

它不一定是,至少在Oracle和Sql服务器上,这是一个神话。 OP在MySQL中遇到了一些不良行为。你的观点对于MySQL来说可能是正确的,没有什么内在的IN慢。 – erikkallen 2009-10-09 20:33:56

+0

您需要告诉您的数据库管理员,该语句高度依赖于产品。大量的数据库使用IN比使用JOIN或OR更好。我看不出为什么IN()会成为数据库的痛苦的任何特殊原因,它似乎自然提供了大量的索引访问机会。 – 2009-10-09 22:15:55

1
SELECT COUNT(DISTINCT workcode) 
FROM worktag 
inner join tag on worktag.tagid = tag.tagid 
WHERE tag.name='programming' 
0

我猜的优化做了一些不好的猜测。用内连接替换查询可能会有所帮助。

1

对于子查询,即使是独立的,MySQL通常也做得不好。讨论连接的海报是正确的 - 如果您有选择,请使用连接。如果你不能轻松使用连接(例如,foo.x in(从y = xxx limit 10的bar中选择y)),最好将限制运行到临时IN MEMORY表中并使用连接。

如果您使用的MySQL很多,请使用EXPLAIN,您会看到它是如何使用索引等的。