Exclua todos, exceto os primeiros n da tabela do banco de dados em SQL

85

Qual é a melhor maneira de excluir todas as linhas de uma tabela no sql, mas manter n número de linhas no topo?

Riri
fonte

Respostas:

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

Editar:

Chris traz um bom resultado de desempenho, já que a consulta TOP 10 seria executada para cada linha. Se for uma coisa única, então pode não ser tão importante, mas se for uma coisa comum, então eu olhei mais de perto.

Cory Foy
fonte
6
Apenas uma nota que você pode resolver o problema de desempenho da subconsulta criando uma tabela temporária manualmente (assumindo que esta é uma operação rara) ou escrevendo a consulta DELETE FROM Table WHERE ID NOT IN (SELECT id FROM (SELECT TOP 10 ID FROM Table) AS x)para forçar o MySQL a criar uma tabela temporária.
Michael Mior
Obrigado. Isso
salvou minha
1
A subconsulta é executada várias vezes, é verdade? stackoverflow.com/questions/18790796/…
djluis
5
@ Daniel Schaffer Não parece que eles tenham nenhum problema de banco de dados ou lógica de negócios. Parece uma política de retenção totalmente normal.
Hejazzman
33

Eu selecionaria ID coluna (s) o conjunto de linhas que você deseja manter em uma tabela temporária ou variável de tabela. Em seguida, exclua todas as linhas que não existem na tabela temporária. A sintaxe mencionada por outro usuário:

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

Tem um problema potencial. A consulta "SELECT TOP 10" será executada para cada linha da tabela, o que pode ser um grande impacto no desempenho. Você deseja evitar fazer a mesma consulta repetidamente.

Essa sintaxe deve funcionar, com base no que você listou como sua instrução SQL original:

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
Chris Miller
fonte
3
insert into #nuke(Nuke) ...provavelmente deveria ser: insert into #nuke(NukeID) ...Além disso, o nome nuke é confuso porque você está tentando NÃO deletar essas linhas. nuke é provavelmente nomeado após o fato de que será excluído.
Erno
12

Referência futura para quem não usa MS SQL.

No PostgreSQL, use ORDER BYe em LIMITvez de TOP.

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

MySQL - bem ...

Erro - Esta versão do MySQL ainda não suporta subconsulta 'LIMIT & IN / ALL / ANY / SOME'

Acho que ainda não.

Simurr
fonte
5

Acho que usar uma tabela virtual seria muito melhor do que uma cláusula IN ou uma tabela temporária.

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
Tim Wilson
fonte
2

Não sei sobre outros sabores, mas o MySQL DELETE permite LIMIT.

Se você pudesse ordenar as coisas de forma que as n linhas que deseja manter fiquem no final, você poderia fazer um DELETE FROM tabela LIMIT tablecount-n.

Editar

Oooo. Acho que gosto mais da resposta de Cory Foy , presumindo que funcione no seu caso. Meu caminho parece um pouco desajeitado em comparação.

Mark Biek
fonte
2

Isso realmente vai ser específico do idioma, mas provavelmente usaria algo como o seguinte para o servidor SQL.

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

se você não se preocupa com o número exato de linhas, sempre há

DELETE TOP 90 PERCENT FROM dTABLE;
Noé
fonte
1
Nenhum desses funciona. A questão pergunta como manter apenas as primeiras N linhas em uma tabela. Ambos os exemplos mantêm apenas as N linhas inferiores.
Chris
1
Funciona bem em MSSQL. Basta adicionar uma classificação para excluir a parte inferior em vez da parte superior?
MeanGreen de
2

Aqui está como eu fiz. Este método é mais rápido e simples:

Exclua tudo, exceto n superior da tabela de banco de dados em MS SQL usando o comando OFFSET

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

Substitua IDpela coluna pela qual você deseja classificar. Substitua o número a seguir OFFSETpelo número de linhas que deseja manter. Escolha DESCou ASC- o que for adequado ao seu caso.

Harvey
fonte
Não seria deslocamento o número de linhas que você deseja manter neste caso?
NapkinBob de
@NapkinBob Sim.
Harvey,
0

Eu iria resolver isso usando a técnica abaixo. O exemplo espera uma tabela de artigo com um id em cada linha.

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

Edit: Muito lento para responder minha própria pergunta ...

Riri
fonte
0

Refatorado?

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

editar: tentei os dois no analisador de consultas, a resposta atual é acelerada (maldita ordem por ...)

Shawn
fonte
0

A melhor maneira seria inserir as linhas que você deseja em outra tabela, remover a tabela original e então renomear a nova tabela para que tenha o mesmo nome da tabela antiga

SQLMenace
fonte
Por que isso é melhor? Mais rápido? Requer alguns comandos extras para realizar.
MeanGreen,
0

Eu tenho um truque para evitar a execução da TOPexpressão para cada linha. Podemos combinar TOPcom MAXpara obter o MaxIdque queremos manter. Então nós simplesmente apagar tudo maior do que 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

Nota: É importante usar ORDER BYao declarar MaxIdpara garantir que os resultados adequados sejam consultados.

Clamchoda
fonte