仅当父行没有子项时才选择父行

时间:2011-07-19 19:14:28

标签: mysql sql

我有一个MySQL数据库,其中表A与表B有一对多的关系,我想选择表B中表A中没有子项的所有行。我试过使用

SELECT id FROM A WHERE NOT EXISTS (SELECT * FROM B WHERE B.id=A.id)

SELECT id FROM A LEFT JOIN B ON A.id=B.id WHERE B.id IS NULL

这两个似乎都很慢。是否有更快的查询来实现同样的目标?

如果这是相关的,在我的数据库表A中有大约500,000行,而表B有大约3到4百万行。

编辑:对于我数据库中的实际表格,解释给了我:

+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+
| id | select_type        | table            | type  | possible_keys | key                       | key_len | ref  | rows    | Extra                    |
+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+
|  1 | PRIMARY            | frontend_form471 | index | NULL          | frontend_form471_61a633e8 | 32      | NULL |  671927 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | SchoolData       | index | PRIMARY       | PRIMARY                   | 49      | NULL | 3121110 | Using where; Using index |
+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+

select number from frontend_form471 where not exists (select * from SchoolData where SchoolData.`f471 Application Number`=frontend_form471.number)

+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+
| id | select_type | table            | type  | possible_keys | key                       | key_len | ref  | rows    | Extra                                          |
+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+
|  1 | SIMPLE      | frontend_form471 | index | NULL          | frontend_form471_61a633e8 | 32      | NULL |  671927 | Using index; Using temporary                   |
|  1 | SIMPLE      | SchoolData       | index | PRIMARY       | PRIMARY                   | 49      | NULL | 3121110 | Using where; Using index; Not exists; Distinct |
+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+

select distinct number from frontend_form471 left join SchoolData on frontend_form471.number=SchoolData.`f471 Application Number` where SchoolData.`f471 Application Number` is NULL

在我的情况下,frontend_form471是表A,而SchoolData是表B

Edit2:在我的数据库中的表B(SchoolData)中,id是两部分主键的第一部分,因此它被编入索引,并且B中仍有多个具有相同id的条目

7 个答案:

答案 0 :(得分:8)

SELECT id FROM A LEFT OUTER JOIN B ON A.id=B.id WHERE B.id IS NULL
你可以这样做。外连接应该带来一点性能,但不是很多。

新的数据库系统可能会优化您的查询,以便不会有任何区别。

这里正确的方法是缓存!如果可能的话,尝试查询cacher和应用程序级缓存。

当然你需要适当的索引。

并且恰当地意思是在两个表上并且最好是哈希索引,因为它将具有与具有对数的任何树的静态查找时间

尝试在查询之前添加一个解释,看看究竟是什么导致这种情况变慢。

如果你真的需要这么快,你可能会重新制定你的数据结构。

你可能会创建一个触发器来标记表A中的标志,表中是否有相应的条目。当然这个id数据冗余,但有时它值得。把它想象成缓存。

最后一个想法:你可以尝试SELECT id FROM A WHERE id NOT IN (SELECT id FROM B)它可能会快一点,因为不需要实际连接,但是它也可能更慢,因为set中的查找将是完整扫描。我不确定如何处理,但值得一试。

答案 1 :(得分:1)

无论你怎么看,它都会变慢。最糟糕的表现将是一个完整的交叉联盟,创造2万亿潜在的匹配(4 mill * 500k)。

第二个很可能表现得更快,因为它只是一个查询。

答案 2 :(得分:1)

你可以尝试

SELECT id FROM A WHERE A.id NOT IN (SELECT id FROM B)

但我不知道这会更快。我会先尝试左连接。我认为你的问题更多地与索引有关。你是否在两个id字段都有索引。

答案 3 :(得分:1)

您的索引很差。

对于所有表单(EXISTS,IN,LEFT JOIN),您应该在两个表中具有id的索引

答案 4 :(得分:0)

确保A.id上有索引,B.id上有另一个索引。

看起来有点奇怪的是你和A.id一起加入A.id. B.id是A的外键还是B的主键?

答案 5 :(得分:0)

如果您的架构是这样的:

CREATE TABLE b(
    id int,
    value varchar(255)
)

CREATE TABLE a(
    id int,
    father_id int,
    value varchar(255)
)

如果你想要表A中没有子项的表A的所有行,为什么你不尝试这样的事情:

SELECT * FROM B WHERE id NOT IN (SELECT father_id FROM A GROUP BY father_id)

我没有测试过,但我觉得它更糟糕了。记得在id

上加上索引

希望这有帮助

答案 6 :(得分:0)

为什么不尝试空值而不是NULL。在SQL中,与任何其他值相比,NULL值永远不会为真,即使是NULL。包含NULL的表达式始终生成NULL值,除非文档中对表达式中涉及的运算符和函数另有说明。