我有一个MySQL表,它看起来(非常简化)如下:
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是最近的)。
是否有一种方法可以选择将level设置为特定值的所有行,然后删除单个查询中的最新行之外的所有行?我该如何开始解决这个问题呢?
(正如我所说,这是一个非常简化的表格,所以请不要试图讨论这个表的可能设计问题。我移走了一些柱子。它是按照PSR-3测井标准设计的,我不认为有一种简单的方法可以改变这种情况。我想解决的是如何从一个表中选择,然后删除同一表中的所有行(除了一些行)。我只有MySQL的中级知识。)
(谢谢你把我推向正确的方向:)
编辑:
数据库版本为/usr/sbin/mysqld Ver 8.0.18-0ubuntu0.19.10.1,用于Linux on x86_64 ((Ubuntu))
发布于 2019-12-28 14:06:10
您可以使用ROW_NUMBER()解析函数(如使用DB版本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是您的主键列)。
发布于 2019-12-28 14:52:32
你可以这样做:
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;发布于 2019-12-28 15:34:09
如果您有一个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()子查询和连接。
如果确实需要使用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)上有一个索引。
https://stackoverflow.com/questions/59511518
复制相似问题