当涉及到数据库时,我是一个相对新手。我们正在使用MySQL,我目前正在尝试加速一条似乎需要很长时间运行的SQL语句。我在SO上搜索了类似的问题,但没有找到。
目标是从表A中删除所有具有与表B匹配的ID的行。
我目前正在执行以下操作:
DELETE FROM a WHERE EXISTS (SELECT b.id FROM b WHERE b.id = a.id);
表a中大约有10万行,表b中大约有2.2万行。列'id'是两个表的主键。 |
在我的测试环境——Pentium D, XP SP3, 2GB内存, MySQL 5.0.67上,这个语句大约需要3分钟的运行时间。这似乎对我来说太慢了。也许不是,但我希望能加快速度。是否有更好/更快的方法来完成这个任务? |
编辑:
以下是一些可能有帮助的额外信息。表A和表B具有相同的结构,因为我已经按照以下方式创建了表B:
CREATE TABLE b LIKE a;
Table a(以及表b)有一些索引,可以帮助加快对其进行的查询。再次说明,我在数据库方面是一个相对初学者,仍在学习中。我不知道这对事情有多大影响(如果有的话)。我认为它会产生影响,因为索引也需要清理,对吧?我还想知道是否有其他数据库设置可能会影响速度。
另外,我正在使用INNO DB。
这里有一些可能对您有帮助的附加信息。
表A的结构类似于以下内容(我稍微进行了清理):
DROP TABLE IF EXISTS `frobozz`.`a`;
CREATE TABLE `frobozz`.`a` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`fk_g` varchar(30) NOT NULL,
`h` int(10) unsigned default NULL,
`i` longtext,
`j` bigint(20) NOT NULL,
`k` bigint(20) default NULL,
`l` varchar(45) NOT NULL,
`m` int(10) unsigned default NULL,
`n` varchar(20) default NULL,
`o` bigint(20) NOT NULL,
`p` tinyint(1) NOT NULL,
PRIMARY KEY USING BTREE (`id`),
KEY `idx_l` (`l`),
KEY `idx_h` USING BTREE (`h`),
KEY `idx_m` USING BTREE (`m`),
KEY `idx_fk_g` USING BTREE (`fk_g`),
KEY `fk_g_frobozz` (`id`,`fk_g`),
CONSTRAINT `fk_g_frobozz` FOREIGN KEY (`fk_g`) REFERENCES `frotz` (`g`)
) ENGINE=InnoDB AUTO_INCREMENT=179369 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
我怀疑问题的一部分原因是这个表有很多索引。 表B看起来与表A相似,但只包含列
id
和h
。此外,性能分析结果如下:
starting 0.000018
checking query cache for query 0.000044
checking permissions 0.000005
Opening tables 0.000009
init 0.000019
optimizing 0.000004
executing 0.000043
end 0.000005
end 0.000002
query end 0.000003
freeing items 0.000007
logging slow query 0.000002
cleaning up 0.000002
解决
感谢所有的回复和评论,它们确实让我思考了这个问题。向dotjoe致敬,他通过简单地问一个问题“是否有其他表引用了a.id?”让我远离了这个问题。
问题在于表A上有一个DELETE触发器,它调用了一个存储过程来更新另外两个表C和D。表C在存储过程中与a.id有一个FK关联,在对与该id相关的一些操作完成后,它有一个语句:
DELETE FROM c WHERE c.id = theId;
我查看了EXPLAIN语句,并将其重写为:
EXPLAIN SELECT * FROM c WHERE c.other_id = 12345;
所以,我可以看到这个程序在做什么,并给了我以下信息:
id 1
select_type SIMPLE
table c
type ALL
possible_keys NULL
key NULL
key_len NULL
ref NULL
rows 2633
Extra using where
这告诉我制作它是一个痛苦的操作,因为它将被调用22500次(对于要删除的给定数据集),这就是问题所在。一旦我在那个other_id列上创建了一个索引并重新运行了EXPLAIN,我得到了:
id 1
select_type SIMPLE
table c
type ref
possible_keys Index_1
key Index_1
key_len 8
ref const
rows 1
Extra
实际上,情况好多了,非常棒。
我添加了Index_1,我的删除时间与mattkemp报告的时间一致。这是我在最后一分钟强行加入一些额外功能时犯下的一个非常微妙的错误。正如Daniel所说,大部分建议的替代DELETE/SELECT语句实际上花费的时间基本相同,正如soulmerge所提到的,该语句基本上是我能够根据需要构建的最佳语句。一旦我为这个其他表C提供了索引,我的DELETE就很快了。
事后分析:
从这次练习中得出了两个教训。首先,很明显我没有利用EXPLAIN语句的威力来更好地了解我的SQL查询的影响。那是一个新手的错误,所以我不会因此而自责。我会从这个错误中学习。其次,有问题的代码是“快速完成”的结果,不充分的设计/测试导致这个问题没有更早地出现。如果我生成了几个相当大的测试数据集作为这个新功能的测试输入,我就不会浪费我的时间和你的时间。我的DB端测试缺乏我应用端已经具备的深度。现在我有机会改进它。