在数据库方面,我是一个相对新手。我们正在使用 MySQL,我目前正在尝试加速似乎需要一段时间才能运行的 SQL 语句。我在 SO 上四处寻找类似的问题,但没有找到。
目标是删除表 A 中与表 B 中具有匹配 id 的所有行。
我目前正在执行以下操作:
DELETE FROM a WHERE EXISTS (SELECT b.id FROM b WHERE b.id = a.id);
表 a 中大约有 100K 行,表 b 中大约有 22K 行。'id' 列是两个表的 PK。
这条语句在我的测试机上运行大约需要 3 分钟 - Pentium D, XP SP3, 2GB ram, MySQL 5.0.67。这对我来说似乎很慢。也许不是,但我希望加快速度。有没有更好/更快的方法来实现这一点?
编辑:
一些可能有用的附加信息。表 A 和 B 的结构与我为创建表 B 所做的以下操作相同:
CREATE TABLE b LIKE a;
表 a(以及表 b)有一些索引来帮助加快对其进行的查询。同样,我是 DB 工作的相对新手,仍在学习。我不知道这对事物有多大影响,如果有的话。我认为它确实有效果,因为索引也必须清理,对吧?我还想知道是否有任何其他可能影响速度的数据库设置。
另外,我正在使用 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 看起来与表 B 相似,但它只包含列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 TRIGGER,它调用存储过程来更新另外两个表 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 列上创建了一个 INDEX 并重新运行 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 查询的影响。这是一个菜鸟的错误,所以我不会为此而自责。我会从那个错误中吸取教训。其次,有问题的代码是“快速完成”心态的结果,而设计/测试不足导致这个问题没有尽快出现。如果我生成了几个相当大的测试数据集作为这个新功能的测试输入,我不会浪费我和你的时间。我在数据库方面的测试缺乏我的应用程序方面的深度。现在我有机会改进它。