删除数据库中除最后日期之外的所有项目
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。
如果确实需要使用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
在这里您可能希望在 (level, time)
上有一个索引。
我有一个 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。
如果确实需要使用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
在这里您可能希望在 (level, time)
上有一个索引。