当前位置:  开发笔记 > 后端 > 正文

SQL查询:删除表中除最新N之外的所有记录?

如何解决《SQL查询:删除表中除最新N之外的所有记录?》经验,为你挑选了6个好方法。

是否可以构建一个单独的mysql查询(没有变量)来删除表中的所有记录,除了最新的N(按id desc排序)?

像这样的东西,只有它不起作用:)

delete from table order by id ASC limit ((select count(*) from table ) - N)

谢谢.



1> Alex Barrett..:

您不能以这种方式删除记录,主要问题是您不能使用子查询来指定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
);

中间子查询必需的.没有它我们会遇到两个错误:

    SQL错误(1093):您无法在FROM子句中为更新指定目标表'table' - MySQL不允许您在直接子查询中引用要删除的表.

    SQL错误(1235):此版本的MySQL尚不支持'LIMIT&IN/ALL/ANY/SOME子查询' - 您不能在NOT IN运算符的直接子查询中使用LIMIT子句.

幸运的是,使用中间子查询允许我们绕过这两个限制.


NickC指出,对于某些用例(例如此用例),可以显着优化此查询.我建议您阅读该答案,看看它是否适合您.


Perroloco,我尝试没有foo并得到了这个错误:ERROR 1248(42000):每个派生表必须有自己的别名所以我们的答案,每个派生表必须有自己的别名!
一个问题:什么是"foo"?
好吧,这是有效的 - 但对我来说,不得不诉诸这样的神秘技巧是不够优雅和不满意的.尽管如此仍为+1答案.

2> Nicole..:

我知道我复活了一个很老的问题,但我最近遇到了这个问题,但需要能够很好地扩展到大量问题的东西.没有任何现有的性能数据,因为这个问题引起了很多关注,我想我会发布我发现的内容.

实际工作的解决方案是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
  )

它用于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 |
+-----------+------------------------+----------------------+

有趣的是,该<=方法在整个过程中看到了更好的性能,但实际上越多越好,而不是更糟.


我在4.5年后再次阅读这篇帖子.好的补充!
在SQL和mySQL之间切换时,@ KenPalmer使用SELECT TOP而不是LIMIT

3> Bill Karwin..:

不幸的是,通过其他人给,你不能在所有的答案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

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'

我能想出的最佳答案是分两个阶段完成:

SELECT id FROM mytable ORDER BY id DESC LIMIT n; 

收集id并将它们组成逗号分隔的字符串:

DELETE FROM mytable WHERE id NOT IN ( ...comma-separated string... );

(通常将逗号分隔列表插入到SQL语句中会引入一些SQL注入的风险,但在这种情况下,值不是来自不受信任的源,它们是来自数据库本身的id值.)

注意:虽然这不能在单个查询中完成工作,但有时一个更简单,可以完成的解决方案是最有效的.



4> Quassnoi..:
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



5> Justin Wigna..:

如果您的id是增量的,那么使用类似的东西

delete from table where id < (select max(id) from table)-N


这个好技巧中的一个大问题是:连续出版物并不总是连续的(例如,当有回滚时).

6> Paolo..:

要删除除最后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;

这种方法的好处是性能:我已经在具有大约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
);

需要0.55秒。大约7倍。

测试环境:2011年末配备SSD的i7 MacBookPro上的mySQL 5.5.25

推荐阅读
小白也坚强_177
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有