从 SQL 中的数据库表中删除除前 n 之外的所有内容

Delete all but top n from database table in SQL

提问人:Riri 提问时间:9/6/2008 更新时间:12/8/2020 访问量:106159

问:

从 sql 中的表中删除所有行但将 n 行数保留在顶部的最佳方法是什么?

SQL格式

评论


答:

93赞 Cory Foy 9/6/2008 #1
DELETE FROM Table WHERE ID NOT IN (SELECT TOP 10 ID FROM Table)

编辑:

Chris 提出了一个很好的性能问题,因为 TOP 10 查询将针对每一行运行。如果这是一次性的事情,那么它可能没什么大不了的,但如果这是一件常见的事情,那么我确实仔细研究了它。

评论

6赞 Michael Mior 7/20/2011
请注意,您可以通过手动创建临时表(假设这是一个不频繁的操作)或编写查询以强制MySQL创建临时表来解决子查询性能问题。DELETE FROM Table WHERE ID NOT IN (SELECT id FROM (SELECT TOP 10 ID FROM Table) AS x)
1赞 djluis 10/27/2016
子查询运行多次,是真的吗?stackoverflow.com/questions/18790796/......
5赞 Hejazzman 5/18/2017
@Daniel Schaffer听起来他们没有任何数据库或业务逻辑问题。听起来像是一个完全正常的保留策略。
0赞 Riosant 4/15/2021
如果我的表中没有主键怎么办?
0赞 Siwei 2/26/2022
在 Postgres (9.5.x) 中不起作用
2赞 Mark Biek 9/6/2008 #2

我不知道其他风格,但MySQL DELETE允许LIMIT。

如果你可以对东西进行排序,使你想要保留的 n 行位于底部,那么你可以执行 DELETE FROM table LIMIT tablecount-n。

编辑

哎呀。我想我更喜欢 Cory Foy 的答案,假设它适用于您的情况。相比之下,我的方式感觉有点笨拙。

0赞 Riri 9/6/2008 #3

我会使用下面的技术来解决它。该示例需要一个行都有一个 id 的文章表。

Delete article where id not in (select top 1000 id from article)

编辑:回答我自己的问题太慢了......

33赞 Chris Miller 9/6/2008 #4

我会选择 ID 列,即要保留到临时表或表变量中的行集。然后删除临时表中不存在的所有行。其他用户提到的语法:

DELETE FROM Table WHERE ID NOT IN (SELECT TOP 10 ID FROM Table)

存在潜在问题。将对表中的每一行执行“SELECT TOP 10”查询,这可能会对性能造成巨大影响。您希望避免一遍又一遍地进行相同的查询。

此语法应该有效,基于您列出的原始 SQL 语句:

create table #nuke(NukeID int)

insert into #nuke(Nuke) select top 1000 id from article

delete article where not exists (select 1 from nuke where Nukeid = id)

drop table #nuke

评论

4赞 Emond 12/6/2016
insert into #nuke(Nuke) ...可能应该是: 此外,nuke这个名字令人困惑,因为您试图不删除这些行。Nuke可能是在它将被删除的事实之后命名的。insert into #nuke(NukeID) ...
0赞 Shawn 9/6/2008 #5

重构了吗?

Delete a From Table a Inner Join (
    Select Top (Select Count(tableID) From Table) - 10) 
        From Table Order By tableID Desc
) b On b.tableID = A.tableID

编辑:在查询分析器中尝试了它们,当前答案是快速的(该死的顺序......

0赞 SQLMenace 9/6/2008 #6

更好的方法是将您想要的行插入到另一个表中,删除原始表,然后重命名新表,使其与旧表同名

评论

0赞 MeanGreen 4/17/2015
为什么这样更好?更快?需要几个额外的命令才能完成。
14赞 Simurr 9/6/2008 #7

为那些不使用 MS SQL 的用户提供未来的参考。

在 PostgreSQL 中,使用 和 代替 .ORDER BYLIMITTOP

DELETE FROM table
WHERE id NOT IN (SELECT id FROM table ORDER BY id LIMIT n);

MySQL——嗯......

错误 -- 此版本的 MySQL 尚不支持“LIMIT & IN/ALL/ANY/SOME 子查询'

我猜还没有。

4赞 Noah 2/4/2009 #8

这确实是特定于语言的,但我可能会对 SQL Server 使用如下所示的内容。

declare @n int
SET @n = SELECT Count(*) FROM dTABLE;
DELETE TOP (@n - 10 ) FROM dTable

如果你不关心确切的行数,总有

DELETE TOP 90 PERCENT FROM dTABLE;

评论

1赞 Chris 11/28/2011
这些都不起作用。该问题询问如何仅保留表中的前 N 行。这两个示例都只保留底部的 N 行。
1赞 MeanGreen 4/17/2015
在MSSQL中工作正常。只是添加排序以删除底部而不是顶部?
0赞 Nick Gallimore 1/5/2023
当然,如果您可以按...我的桌子很大
6赞 Tim Wilson 11/5/2012 #9

我认为使用虚拟表会比 IN 子句或临时表好得多。

DELETE 
    Product
FROM
    Product
    LEFT OUTER JOIN
    (
        SELECT TOP 10
            Product.id
        FROM
            Product
    ) TopProducts ON Product.id = TopProducts.id
WHERE
    TopProducts.id IS NULL
11赞 Hrvoje 4/25/2019 #10

这是我是如何做到的。此方法更快、更简单:

使用 OFFSET 命令从 MS SQL 的数据库表中删除除前 n 之外的所有内容

WITH CTE AS
    (
    SELECT  ID
    FROM    dbo.TableName
    ORDER BY ID DESC
    OFFSET 11 ROWS
    )
DELETE CTE;

替换为要排序的列。 将 number after 替换为要保留的行数。 选择或 - 任何适合您的情况。IDOFFSETDESCASC

评论

0赞 NapkinBob 10/2/2020
在这种情况下,偏移量不是您要保留的行数吗?
0赞 Hrvoje 10/2/2020
@NapkinBob是的。
1赞 clamchoda 8/18/2020 #11

我有一个技巧可以避免对每一行执行表达式。我们可以结合以获得我们想要保留的东西。然后我们只需删除大于 的所有内容。TOPTOPMAXMaxIdMaxId

-- Declare Variable to hold the highest id we want to keep. 
DECLARE @MaxId as int = (
SELECT MAX(temp.ID)
FROM (SELECT TOP 10 ID FROM table ORDER BY ID ASC) temp
)

-- Delete anything greater than MaxId. If MaxId is null, there is nothing to delete.
IF @MaxId IS NOT NULL
    DELETE FROM table WHERE ID > @MaxId

注意:在声明时使用这一点很重要,以确保查询正确的结果。ORDER BYMaxId