我有一个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的条目
答案 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值,除非文档中对表达式中涉及的运算符和函数另有说明。