快速查询选择不在MySQL中的另一个表中的所有记录

时间:2011-04-21 11:32:58

标签: mysql database performance

我有一个选择节点表的查询,然后将标题表连接到它。这是通过首先加入节点ID和标题ID的中间表来完成的,该表允许前两个表之间的多对多关系。两个连接都是内部的,因此只选择具有正确配置和现有标题的节点。我相信这一切都是干净而有效的 - 问题如下:

还有第四个表为节点提供了简单的层次结构; node_parents。每行有两个字段;节点ID和充当该节点的父节点的节点ID(node_id和parent_id)。某些节点没有在此数据库中配置子节点(即节点本身未在node_parents表的任何行中标记为父节点) - 这些是我正在尝试选择的节点。

这些无子节点的附加条件是它们配置了特定的标题 - 因此子查询最初从node_titles中选择,然后在内部加入node_parents。子查询还具有GROUP BY,因为某些节点是多个节点的父节点,因此它们的node_id将不必要地在结果中多次出现。我还应该指出,因为node_parents的主键是node_id和parent_id的组合。

查询:

SELECT  `nodes`.`node_id`,
        `titles`.`title`
FROM `nodes`
INNER JOIN `node_titles`
ON `nodes`.`node_id` = `node_titles`.`node_id`
INNER JOIN `titles`
ON `node_titles`.`title_id` = `titles`.`title_id`
WHERE `nodes`.`node_id` NOT IN
    (
    SELECT `node_titles`.`node_id`
    FROM `node_titles`
    INNER JOIN `node_parents`
    ON `node_titles`.`node_id` = `node_parents`.`parent_id`
    WHERE `node_titles`.`title_id` = 1
    GROUP BY `node_titles`.`node_id`
    )
AND `titles`.`title_id` = 1

表格大小: nodes = ~32,000 node_titles = ~49,000 titles = 3 node_parents = ~55,000

查询大约需要16分钟才能完成。任何人都可以提供任何指示吗?我试过分析查询 - 它没有任何长时间挂起,但它确实重复了这个循环,看起来像每个选定的行:

| executing                      | 0.000005 |
| Copying to tmp table           | 0.515815 |
| Sorting result                 | 0.000053 |
| Sending data                   | 0.000028 |

我也试过抛弃子查询并使用LEFT JOIN和WHERE foo IS NOT NULL,但这仍然需要很长时间才能处理 - 探测器声称“复制到tmp表”的时间约为180秒。

最终我怀疑这可能是一个索引问题 - 但无论哪种方式,我都会欣赏那些不会质疑查询实现的答案,除非他们追求可能的减速原因(例如,是的,标题和节点)必须处于多对多的关系中)。感谢所有人,以及有关要求的进一步信息!

2 个答案:

答案 0 :(得分:2)

从子查询中删除GROUP BY

SELECT  nodes.node_id,
        titles.title
FROM    nodes n
INNER JOIN
        node_titles nt
ON      nt.node_id = n.node_id
INNER JOIN
        titles t
ON      t.title_id = nt.title_id
WHERE   n.node_id NOT IN
        (
        SELECT  nti.node_id
        FROM    node_titles nti
        INNER JOIN 
                node_parents npi
        ON      npi.parent_id = nt.node_id
        WHERE   nti.title_id = 1
        )

创建以下索引:

node_titles (node_id, title_id)
titles (title_id)
node_parents (parent_id)

<强>更新

试试这个:

SELECT  nodes.node_id,
        titles.title
FROM    nodes n
INNER JOIN
        node_titles nt
ON      nt.node_id = n.node_id
        AND nt.title_id = 1
INNER JOIN
        titles t
ON      t.title_id = nt.title_id
WHERE   n.node_id NOT IN
        (
        SELECT  parent_id
        FROM    node_parents
        )

答案 1 :(得分:1)

根据我的经验,MySql往往会出现子查询问题。试试这个

SELECT  nodes.node_id,
        titles.title
FROM    nodes b
INNER JOIN
        node_titles nt
ON      nt.node_id = n.node_id
INNER JOIN
        titles t
ON      t.title_id = nt.title_id
LEFT OUTER JOIN   
        (
        SELECT  nti.node_id
        FROM    node_titles nti
        INNER JOIN 
                node_parents npi
        ON      npi.parent_id = nt.node_id
        WHERE   nti.title_id = 1
        ) ThisTable on n.node_id = ThisTable.node_id
 WHERE ThisTable.node_id is null