Удалить все, кроме верхнего n, из таблицы базы данных в SQL

85

Какой лучший способ удалить все строки из таблицы в sql, но сохранить n строк вверху?

Рири
источник

Ответы:

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

Редактировать:

Крис показывает хорошее снижение производительности, поскольку запрос TOP 10 будет выполняться для каждой строки. Если это разовая вещь, то, возможно, это не такая уж большая проблема, но если это обычная вещь, то я действительно присмотрелся к ней.

Кори Фой
источник
6
Просто обратите внимание, что вы можете решить проблему производительности подзапроса, создав временную таблицу вручную (при условии, что это нечастая операция) или написав запрос, DELETE FROM Table WHERE ID NOT IN (SELECT id FROM (SELECT TOP 10 ID FROM Table) AS x)чтобы заставить MySQL создать временную таблицу.
Майкл Миор
Спасибо. Это
спасло
1
Подзапрос выполняется несколько раз, это правда? stackoverflow.com/questions/18790796/…
djluis
5
@ Daniel Schaffer: Похоже, у них проблемы с базой данных или бизнес-логикой. Похоже на совершенно нормальную политику хранения.
Hejazzman
33

Я бы выбрал столбцы идентификаторов - набор строк, которые вы хотите сохранить во временной таблице или табличной переменной. Затем удалите все строки, которых нет во временной таблице. Синтаксис, упомянутый другим пользователем:

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 BYи LIMITвместо TOP.

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

MySQL - ну ...

Ошибка - эта версия MySQL еще не поддерживает подзапрос LIMIT & IN / ALL / ANY / SOME.

Еще не думаю.

Simurr
источник
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 строк, которые вы хотите сохранить, находились внизу, вы могли бы выполнить УДАЛЕНИЕ ИЗ таблицы LIMIT tablecount-n.

редактировать

Оооо. Я думаю, что мне больше нравится ответ Кори Фоя , если он работает в вашем случае. По сравнению с этим мой путь кажется немного неуклюжим.

Марк Бик
источник
2

Это действительно будет зависеть от языка, но я бы, вероятно, использовал что-то вроде следующего для SQL-сервера.

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

Вот как я это сделал. Этот способ быстрее и проще:

Удалите все, кроме верхнего n, из таблицы базы данных в MS SQL с помощью команды OFFSET

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

Замените IDстолбцом, по которому вы хотите отсортировать. Заменить номер послеOFFSET числом строк, которые вы хотите сохранить. Выберите DESCили ASC- что подходит для вашего случая.

Харви
источник
Не будет ли смещение количества строк, которые вы хотите сохранить в этом случае?
Салфетка Боб
@NapkinBob Да.
Харви
0

Я бы решил это, используя методику ниже. В этом примере ожидается таблица статей с идентификатором в каждой строке.

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

Лучшим способом было бы вставить нужные вам строки в другую таблицу, удалить исходную таблицу и затем переименовать новую таблицу, чтобы она имела то же имя, что и старая таблица.

SQLMenace
источник
Почему так лучше? Быстрее? Для выполнения требуется несколько дополнительных команд.
MeanGreen
0

У меня есть трюк, чтобы не выполнять TOPвыражение для каждой строки. Мы можем комбинировать TOPс, MAXчтобы получить то, 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чтобы обеспечить запрос правильных результатов.

Clamchoda
источник