【问题标题】:Delete all rows except 100 most recent ones删除除 100 个最近的行之外的所有行
【发布时间】:2015-09-24 01:25:11
【问题描述】:

我正在使用 MS SQL 数据库,我有一个名为“Logs”的表,如下所示:

记录一个接一个地添加到这里,所以几天/几周后表格变得相当大。

我需要定期做一些清理工作。 IE。我需要一个 SQL 查询来删除旧行并仅保留此表中最近的 100 条记录。

我同意最好让它“删除早于 some_date 的记录”...以前是这样的,但客户希望它不同:( 所以...我们来了。

顺便说一句:我对人们对这个问题持否定态度有点失望。有什么问题吗?... 想象一下:这个问题已经产生了 4 个答案!!! ......一个人决定对此表示否定!现在我真的不知道该怎么想......这里有奇怪的人:(

【问题讨论】:

  • 你需要做什么?删除旧记录还是删除除最近 100 条以外的所有记录?如果你在一秒钟内获得 101 条记录会发生什么?这是否意味着你会错过第一条记录?
  • 是否有 ID 或任何使行唯一的东西?
  • 现在磁盘空间太便宜了,我会反对这种大小的行,不需要删除任何东西。如果您确实需要删除,我不会基于“留下这么多记录”来删除,而是“删除早于 x 日期的记录”。
  • @Kritner :我不明白你的问题,但我只能说:通常在凌晨 3:00 某处发生清理工作,通常在那个时间没有新记录到达......而且,是的,我同意应该根据日期而不是数字来删除记录:)

标签: sql sql-server tsql datetime sql-delete


【解决方案1】:

虽然我同意其他人的观点,这可能不是要走的路,但无论如何,这里有一种方法:

;WITH keepers AS
(   SELECT TOP 100 [DateTime]
    FROM dbo.Logs
    ORDER BY [DateTime] DESC )
DELETE FROM dbo.Logs a
WHERE NOT EXISTS ( SELECT 1 FROM keepers b WHERE b.[DateTime] = a.[DateTime] )

【讨论】:

  • 它不起作用:它显示“Msg 156, Level 15, State 1, Line 1 关键字'NOT'附近的语法不正确。”
  • 好的,经过一些小的更正后它可以工作,但我每次执行查询时都会计算 101 条记录...
  • 任何重复的 [DateTime] 值?
  • 嗯,可能!现在检查为时已晚...我在该表上运行了很多查询...它搞砸了,但我想您可能是对的 :)
【解决方案2】:

您可以使用以下方法之一:

-- offset clause
WITH goners AS (
    SELECT *
    FROM Logs
    ORDER BY DateTime DESC
    OFFSET 100 ROWS 
)
DELETE FROM goners

-- numbered rows
WITH goners AS (
    SELECT ROW_NUMBER() OVER(ORDER BY DateTime DESC) AS rn, Logs.*
    FROM Logs
)
DELETE FROM goners
WHERE rn > 100

-- nth value
-- note that this "keeps" all rows that tie for last place
DELETE FROM Logs
WHERE DateTime < (
    SELECT MIN(DateTime)
    FROM (
        SELECT TOP 100 DateTime
        FROM Logs
        ORDER BY DateTime DESC
    ) AS x
)

【讨论】:

  • 后者不能与 [DateTime] 列一起使用吗?
  • @SalmanA :如果有更多记录 DateTime = MIN(DateTime) ...?我猜在执行您的查询(我正在谈论的第一个)之后可能有不到 100 行。对吗?
  • @groenhen:是的,第一种方法不能保证准确地保留 100 条记录。在例如第 100、101 和 102 行具有相同的日期时间。
  • @groenhen 查看修改后的答案
【解决方案3】:

不用NOT EXISTS,只用&gt;=

WITH keepers AS (
    SELECT TOP 100 [DateTime]
    FROM dbo.Logs
    ORDER BY [DateTime] DESC
   )
DELETE FROM dbo.Logs a
    WHERE l.DateTime < (SELECT MIN([DateTime]) FROM keepers);

我不确定在delete 运行时是否有可以添加新行的锁定设置。如果是这样,这仍然是安全的。

您实际上可以在 SQL Server 2012+ 中简化此操作:

DELETE FROM dbo.Logs a
    WHERE l.DateTime < (SELECT [DateTime] 
                        FROM dbo.logs
                        ORDER BY [DateTime]
                        OFFSET 99 FETCH FIRST 1 ROW ONLY
                       );

【讨论】:

  • 好的,经过一些小的更正(缺少括号等)后它可以工作
  • 当数据量约为 40GB 时这项工作是否有效,我继承了一个巨大的数据,其中最大是一个 2 年从未清理过的单个日志表。现在必须清理它并只保存一个月的数据,将其余部分移动到存档(通过另一个副本)
  • @NitinSingh 。 . .是的。您可能需要索引来加快查询速度,如果要删除很多行,您可能需要采用不同的方法。
【解决方案4】:
DECLARE @cutoff DATETIME
SELECT TOP 100 @cutoff = [DateTime] FROM Logs ORDER BY [DateTime] DESC
DELETE FROM Logs WHERE [DateTime] < @cutoff

【讨论】:

    【解决方案5】:

    这对我有用:

    ;with cte as(select top(select count(*) - 100 from table) * from table order by dt)
    delete from cte
    

    【讨论】:

    • 但我认为如果在某个时刻 count(*) 小于 100,即选择负数的行/记录...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-13
    • 2011-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-15
    相关资源
    最近更新 更多