删除数据库中除最后日期之外的所有项目

Delete all items in a database except the last date

我有一个 MySQL table 看起来(非常简单)像这样:

CREATE TABLE `logging` (
  `id` bigint(20) NOT NULL,
  `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `level` smallint(3) NOT NULL,
  `message` longtext CHARACTER SET utf8 COLLATE utf8_general_mysql500_ci NOT NULL
);

我想删除特定 level 的所有行,最后一行除外(time 是最新的)。

有没有办法 select 所有 level 设置为特定值的行,然后在单个 SQL 查询中删除除最新行之外的所有行?我将如何开始解决这个问题?

(正如我所说,这是一个非常简化的 table,因此请不要尝试讨论此 table 可能存在的设计问题。我删除了一些列。它是根据 PSR-3 logging standard 我不认为有一种简单的方法可以改变它。我想解决的是如何从 table 中 select 然后删除除某些行之外的所有行table。我只有 MySQL 的中级知识。)

谢谢你把我推向正确的方向:)

编辑:

数据库版本是 /usr/sbin/mysqld Ver 8.0.18-0ubuntu0.19.10.1 for Linux on x86_64 ((Ubuntu))

您可以使用 ROW_NUMBER() 分析函数(使用数据库版本 8+):

DELETE lg FROM `logging` AS lg
 WHERE lg.`id` IN
      ( SELECT t.`id` 
          FROM
         (
          SELECT t.*,
                 ROW_NUMBER() OVER (ORDER BY `time` DESC) as rn
            FROM `logging` t
       --  WHERE `level` = @lvl -- optionally add this line to restrict for a spesific value of `level`
         ) t
         WHERE t.rn > 1
       )

删除除最后插入的行之外的所有行(考虑到 id 是您的主键列)。

你可以这样做:

SELECT COUNT(time) FROM logging WHERE level=some_level INTO @TIME_COUNT;
SET @TIME_COUNT = @TIME_COUNT-1;
PREPARE STMT FROM 'DELETE FROM logging WHERE level=some_level ORDER BY time ASC LIMIT ?;';
EXECUTE STMT USING @TIME_COUNT;

如果您有 AUTO_INCREMENT id 列 - 我会用它来确定最近的条目。这是一种方法:

delete l
from (
  select l1.level, max(id) as id
  from logging l1
  where l1.level = @level
) m
join logging l
  on  l.level = m.level
  and l.id    < m.id

(level) 上的索引应该会给您带来良好的性能,并将支持 MAX() 子查询和 JOIN。

View on DB Fiddle

如果确实需要使用time列,可以修改查询如下:

delete l
from (
  select l1.level, l1.id
  from logging l1
  where l1.level = @level
  order by l1.time desc, l1.id desc
  limit 1
) m
join logging l
  on  l.level = m.level
  and l.id   <> m.id

View on DB Fiddle

在这里您可能希望在 (level, time) 上有一个索引。