是否可以构建一个单独的mysql查询(没有变量)来删除表中的所有记录,除了最新的N(按id desc排序)?
像这样的东西,只有它不起作用:)
delete from table order by id ASC limit ((select count(*) from table ) - N)
Run Code Online (Sandbox Code Playgroud)
谢谢.
Ale*_*ett 128
您不能以这种方式删除记录,主要问题是您不能使用子查询来指定LIMIT子句的值.
这工作(在MySQL 5.0.67中测试):
DELETE FROM `table`
WHERE id NOT IN (
SELECT id
FROM (
SELECT id
FROM `table`
ORDER BY id DESC
LIMIT 42 -- keep this many records
) foo
);
Run Code Online (Sandbox Code Playgroud)
中间子查询是必需的.没有它我们会遇到两个错误:
幸运的是,使用中间子查询允许我们绕过这两个限制.
NickC指出,对于某些用例(例如此用例),可以显着优化此查询.我建议您阅读该答案,看看它是否适合您.
Nic*_*ole 99
我知道我复活了一个很老的问题,但我最近遇到了这个问题,但需要能够很好地扩展到大量问题的东西.没有任何现有的性能数据,因为这个问题引起了很多关注,我想我会发布我发现的内容.
实际工作的解决方案是Alex Barrett的NOT IN双子查询/方法(类似于Bill Karwin的)和Quassnoi的LEFT JOIN方法.
遗憾的是,上述两种方法都会创建非常大的中间临时表,并且随着未删除的记录数量变大,性能会迅速降低.
我决定使用Alex Barrett的双子查询(谢谢!)但使用<=而不是NOT IN:
DELETE FROM `test_sandbox`
WHERE id <= (
SELECT id
FROM (
SELECT id
FROM `test_sandbox`
ORDER BY id DESC
LIMIT 1 OFFSET 42 -- keep this many records
) foo
)
Run Code Online (Sandbox Code Playgroud)
它用于OFFSET获取第N条记录的id 并删除该记录和所有先前的记录.
由于排序已经是这个问题的假设(ORDER BY id DESC),<=因此非常适合.
它更快,因为子查询生成的临时表只包含一个记录而不是N个记录.
我在两个测试用例中测试了上述三种工作方法和新方法.
两个测试用例使用10000个现有行,而第一个测试保留9000(删除最旧的1000),第二个测试保持50(删除最旧的9950).
+-----------+------------------------+----------------------+
| | 10000 TOTAL, KEEP 9000 | 10000 TOTAL, KEEP 50 |
+-----------+------------------------+----------------------+
| NOT IN | 3.2542 seconds | 0.1629 seconds |
| NOT IN v2 | 4.5863 seconds | 0.1650 seconds |
| <=,OFFSET | 0.0204 seconds | 0.1076 seconds |
+-----------+------------------------+----------------------+
Run Code Online (Sandbox Code Playgroud)
有趣的是,该<=方法在整个过程中看到了更好的性能,但实际上越多越好,而不是更糟.
不幸的是,通过其他人给,你不能在所有的答案DELETE,并SELECT从表中给出相同的查询.
DELETE FROM mytable WHERE id NOT IN (SELECT MAX(id) FROM mytable);
ERROR 1093 (HY000): You can't specify target table 'mytable' for update
in FROM clause
Run Code Online (Sandbox Code Playgroud)
MySQL也不能支持LIMIT子查询.这些是MySQL的局限性.
DELETE FROM mytable WHERE id NOT IN
(SELECT id FROM mytable ORDER BY id DESC LIMIT 1);
ERROR 1235 (42000): This version of MySQL doesn't yet support
'LIMIT & IN/ALL/ANY/SOME subquery'
Run Code Online (Sandbox Code Playgroud)
我能想出的最佳答案是分两个阶段完成:
SELECT id FROM mytable ORDER BY id DESC LIMIT n;
Run Code Online (Sandbox Code Playgroud)
收集id并将它们组成逗号分隔的字符串:
DELETE FROM mytable WHERE id NOT IN ( ...comma-separated string... );
Run Code Online (Sandbox Code Playgroud)
(通常将逗号分隔列表插入到SQL语句中会引入一些SQL注入的风险,但在这种情况下,值不是来自不受信任的源,它们是来自数据库本身的id值.)
注意:虽然这不能在单个查询中完成工作,但有时一个更简单,可以完成的解决方案是最有效的.
DELETE i1.*
FROM items i1
LEFT JOIN
(
SELECT id
FROM items ii
ORDER BY
id DESC
LIMIT 20
) i2
ON i1.id = i2.id
WHERE i2.id IS NULL
Run Code Online (Sandbox Code Playgroud)
如果您的id是增量的,那么使用类似的东西
delete from table where id < (select max(id) from table)-N
Run Code Online (Sandbox Code Playgroud)
要删除除最后N个记录以外的所有记录,您可以使用下面报告的查询。
这是一个查询,但是有很多语句,因此实际上并不是单个查询的原始查询方式。
另外,由于MySQL中的错误,您还需要一个变量和一个内置的(在查询中)准备好的语句。
希望它仍然有用...
NNN是行保持和theTable是你的工作表。
我假设您有一个名为id的自动递增记录
SELECT @ROWS_TO_DELETE := COUNT(*) - nnn FROM `theTable`;
SELECT @ROWS_TO_DELETE := IF(@ROWS_TO_DELETE<0,0,@ROWS_TO_DELETE);
PREPARE STMT FROM "DELETE FROM `theTable` ORDER BY `id` ASC LIMIT ?";
EXECUTE STMT USING @ROWS_TO_DELETE;
Run Code Online (Sandbox Code Playgroud)
这种方法的好处是性能:我已经在具有大约13,000条记录的本地数据库上测试了查询,保留了最后1000条记录。运行时间为0.08秒。
来自已接受答案的脚本...
DELETE FROM `table`
WHERE id NOT IN (
SELECT id
FROM (
SELECT id
FROM `table`
ORDER BY id DESC
LIMIT 42 -- keep this many records
) foo
);
Run Code Online (Sandbox Code Playgroud)
需要0.55秒。大约7倍。
测试环境:2011年末配备SSD的i7 MacBookPro上的mySQL 5.5.25