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


85

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

Answers:


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

编辑:

克里斯带来了良好的性能影响,因为将对每一行运行TOP 10查询。如果这是一次性的事情,那么可能没什么大不了的,但是,如果这是平常的事情,那么我确实会仔细研究一下。


4
如果某人通常需要删除除前n行以外的所有行,那么我认为他们还有更大的问题要担心。
丹尼尔·谢弗

6
请注意,您可以通过手动创建一个临时表(假设这是一个不常用的操作)或编写查询DELETE FROM Table WHERE ID NOT IN (SELECT id FROM (SELECT TOP 10 ID FROM Table) AS x)来强制MySQL创建一个临时表来解决子查询性能问题。
Michael Mior

谢谢。这是一个生命的救星:)
SI 8

1
子查询多次运行,是吗?stackoverflow.com/questions/18790796/…–
djluis

5
@ Daniel Schaffer听起来好像他们没有任何数据库或业务逻辑问题。听起来像是完全正常的保留政策。
Hejazzman's

33

我将选择要保留到临时表或表变量中的一组行的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

3
insert into #nuke(Nuke) ...应该可能是:insert into #nuke(NukeID) ...nuke这个名称也令人困惑,因为您正试图不删除这些行。nuke可能会被删除而得名。
Erno

12

不使用MS SQL的使用者的将来参考。

在PostgreSQL中,使用ORDER BYLIMIT代替TOP

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

MySQL-好吧...

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

我猜还没有。


5

我认为使用虚拟表比使用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

2

我不了解其他口味,但是MySQL DELETE允许LIMIT。

如果可以对事物进行排序,以使要保留的n行位于底部,则可以执行DELETE FROM表LIMIT tablecount-n。

编辑

哦 我认为我更喜欢Cory Foy的答案,前提是它适合您的情况。相比之下,我的方式有点笨拙。


2

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

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

如果您不在乎确切的行数,那么总会有

DELETE TOP 90 PERCENT FROM dTABLE;

1
这些都不起作用。该问题询问如何仅保留表中的前N行。这两个示例都只保留底部N行。
克里斯,

1
在MSSQL中工作正常。只是添加排序以删除底部而不是顶部?
MeanGreen

2

这是我的方法。此方法更快,更简单:

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

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

替换ID为要排序的列。用OFFSET您要删除的行数替换数字。选择DESCASC-根据您的情况选择。


在这种情况下,是否要保留的行数不会偏移吗?
NapkinBob

@NapkinBob是的。
哈维

0

我将使用以下技术解决该问题。这个例子期待的文章与表ID上的每一行。

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

编辑:太慢,无法回答我自己的问题...


0

重构了吗?

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

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


为什么这样更好?快点?需要几个额外的命令才能完成。
MeanGreen

0

我有一个技巧,可以避免TOP对每一行执行该表达式。我们可以结合起来TOPMAX得到MaxId我们想要保留的东西。然后,我们只删除大于的所有内容MaxId

-- 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 BY在声明MaxId以确保查询正确结果时,务必要使用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.