Вы не можете удалить записи таким образом, основная проблема заключается в том, что вы не можете использовать подзапрос для указания значения предложения LIMIT.
Это работает (проверено в MySQL 5.0.67):
DELETE FROM `table`
WHERE id NOT IN (
SELECT id
FROM (
SELECT id
FROM `table`
ORDER BY id DESC
LIMIT 42
) foo
);
Промежуточного подзапрос будет необходимо. Без него мы столкнемся с двумя ошибками:
- Ошибка SQL (1093): вы не можете указать целевую таблицу 'table' для обновления в предложении FROM - MySQL не позволяет вам ссылаться на таблицу, которую вы удаляете, из прямого подзапроса.
- Ошибка SQL (1235): эта версия MySQL еще не поддерживает подзапрос LIMIT & IN / ALL / ANY / SOME - предложение LIMIT нельзя использовать в прямом подзапросе оператора NOT IN.
К счастью, использование промежуточного подзапроса позволяет обойти оба этих ограничения.
Николь отметила, что этот запрос можно значительно оптимизировать для определенных случаев использования (например, этого). Я также рекомендую прочитать этот ответ, чтобы узнать, подходит ли он вашему.
Я знаю, что воскрешаю довольно старый вопрос, но недавно я столкнулся с этой проблемой, но мне нужно что-то, что хорошо масштабируется до больших чисел . Не было никаких существующих данных о производительности, и, поскольку этому вопросу было уделено довольно много внимания, я решил опубликовать то, что нашел.
Решениями, которые действительно сработали, были двойной подзапрос /
NOT IN
метод Алекса Барретта (аналогичный методу Билла Карвина ) и метод КвасснойLEFT JOIN
.К сожалению, оба вышеперечисленных метода создают очень большие промежуточные временные таблицы, и производительность быстро снижается, поскольку количество записей, которые не удаляются, становится большим.
То, что я выбрал, использует двойной подзапрос Алекса Барретта (спасибо!), Но
<=
вместо этого используетNOT IN
:DELETE FROM `test_sandbox` WHERE id <= ( SELECT id FROM ( SELECT id FROM `test_sandbox` ORDER BY id DESC LIMIT 1 OFFSET 42 -- keep this many records ) foo )
Он использует
OFFSET
для получения идентификатора N- й записи и удаляет эту запись и все предыдущие записи.Поскольку порядок уже является условием этой проблемы (
ORDER BY id DESC
),<=
он идеально подходит.Это намного быстрее, поскольку временная таблица, созданная подзапросом, содержит только одну запись вместо N записей.
Прецедент
Я протестировал три метода работы и новый метод, описанный выше, в двух тестовых случаях.
Оба тестовых примера используют 10000 существующих строк, в то время как первый тест сохраняет 9000 (удаляет самую старую 1000), а второй тест сохраняет 50 (удаляет самые старые 9950).
+-----------+------------------------+----------------------+ | | 10000 TOTAL, KEEP 9000 | 10000 TOTAL, KEEP 50 | +-----------+------------------------+----------------------+ | NOT IN | 3.2542 seconds | 0.1629 seconds | | NOT IN v2 | 4.5863 seconds | 0.1650 seconds | | <=,OFFSET | 0.0204 seconds | 0.1076 seconds | +-----------+------------------------+----------------------+
Что интересно, этот
<=
метод показывает лучшую производительность по всем направлениям, но на самом деле становится лучше, чем больше вы храните, а не хуже.источник
ROW_NUMBER()
: stackoverflow.com/questions/603724/…К сожалению, для всех ответов, данных другими людьми, вы не можете
DELETE
иSELECT
из данной таблицы в том же запросе.DELETE FROM mytable WHERE id NOT IN (SELECT MAX(id) FROM mytable); ERROR 1093 (HY000): You can't specify target table 'mytable' for update in FROM clause
MySQL также не может поддерживать
LIMIT
подзапрос. Это ограничения MySQL.DELETE FROM mytable WHERE id NOT IN (SELECT id FROM mytable ORDER BY id DESC LIMIT 1); ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
Лучший ответ, который я могу придумать, - это сделать это в два этапа:
SELECT id FROM mytable ORDER BY id DESC LIMIT n;
Соберите идентификаторы и превратите их в строку, разделенную запятыми:
DELETE FROM mytable WHERE id NOT IN ( ...comma-separated string... );
(Обычно интерполяция списка, разделенного запятыми, в оператор SQL представляет некоторый риск внедрения SQL, но в этом случае значения не поступают из ненадежного источника, они, как известно, являются целочисленными значениями из самой базы данных.)
примечание: хотя это не позволяет выполнить работу в одном запросе, иногда наиболее эффективным оказывается более простое и готовое решение.
источник
DELETE FROM mytable WHERE id NOT IN (SELECT id FROM mytable ORDER BY id DESC LIMIT 3);
работает нормально.DELETE i1.* FROM items i1 LEFT JOIN ( SELECT id FROM items ii ORDER BY id DESC LIMIT 20 ) i2 ON i1.id = i2.id WHERE i2.id IS NULL
источник
Если ваш идентификатор инкрементный, используйте что-то вроде
delete from table where id < (select max(id) from table)-N
источник
Чтобы удалить все записи, кроме последних N, вы можете использовать запрос, указанный ниже.
Это один запрос, но с множеством инструкций, поэтому на самом деле это не один запрос, как это было задумано в исходном вопросе.
Также вам понадобится переменная и встроенный (в запросе) подготовленный оператор из-за ошибки в MySQL.
Надеюсь, это все равно может быть полезно ...
nnn - это строки, которые нужно сохранить, а theTable - это таблица, над которой вы работаете.
Я предполагаю, что у вас есть запись с автоинкрементом с именем id
SELECT @ROWS_TO_DELETE := COUNT(*) - nnn FROM `theTable`; SELECT @ROWS_TO_DELETE := IF(@ROWS_TO_DELETE<0,0,@ROWS_TO_DELETE); PREPARE STMT FROM "DELETE FROM `theTable` ORDER BY `id` ASC LIMIT ?"; EXECUTE STMT USING @ROWS_TO_DELETE;
Хорошая вещь в этом подходе - производительность : я протестировал запрос в локальной БД с примерно 13 000 записей, сохранив последние 1000. Он работает за 0,08 секунды.
Скрипт из принятого ответа ...
DELETE FROM `table` WHERE id NOT IN ( SELECT id FROM ( SELECT id FROM `table` ORDER BY id DESC LIMIT 42 -- keep this many records ) foo );
Занимает 0,55 секунды. Примерно в 7 раз больше.
Тестовая среда: mySQL 5.5.25 на i7 MacBookPro конца 2011 года с SSD
источник
DELETE FROM table WHERE ID NOT IN (SELECT MAX(ID) ID FROM table)
источник
попробуйте ниже запрос:
DELETE FROM tablename WHERE id < (SELECT * FROM (SELECT (MAX(id)-10) FROM tablename ) AS a)
внутренний подзапрос вернет 10 первых значений, а внешний запрос удалит все записи, кроме 10 лучших.
источник
Что о :
SELECT * FROM table del LEFT JOIN table keep ON del.id < keep.id GROUP BY del.* HAVING count(*) > N;
Он возвращает строки с более чем N строками ранее. Может быть полезно?
источник
Использование id для этой задачи во многих случаях не вариант. Например - таблица с твиттер-статусами. Вот вариант с указанным полем отметки времени.
delete from table where access_time >= ( select access_time from ( select access_time from table order by access_time limit 150000,1 ) foo )
источник
Просто хотел добавить это в смесь для тех, кто использует Microsoft SQL Server вместо MySQL. Ключевое слово Limit не поддерживается MSSQL, поэтому вам нужно использовать альтернативу. Этот код работал в SQL 2008 и основан на этом сообщении SO. https://stackoverflow.com/a/1104447/993856
-- Keep the last 10 most recent passwords for this user. DECLARE @UserID int; SET @UserID = 1004 DECLARE @ThresholdID int -- Position of 10th password. SELECT @ThresholdID = UserPasswordHistoryID FROM ( SELECT ROW_NUMBER() OVER (ORDER BY UserPasswordHistoryID DESC) AS RowNum, UserPasswordHistoryID FROM UserPasswordHistory WHERE UserID = @UserID ) sub WHERE (RowNum = 10) -- Keep this many records. DELETE UserPasswordHistory WHERE (UserID = @UserID) AND (UserPasswordHistoryID < @ThresholdID)
По общему признанию, это не изящно. Если вы можете оптимизировать это для Microsoft SQL, поделитесь своим решением. Благодарность!
источник
Если вам нужно удалить записи, основанные на каком-то другом столбце, то вот решение:
DELETE FROM articles WHERE id IN (SELECT id FROM (SELECT id FROM articles WHERE user_id = :userId ORDER BY created_at DESC LIMIT 500, 10000000) abc) AND user_id = :userId
источник
Это тоже должно сработать:
DELETE FROM [table] INNER JOIN ( SELECT [id] FROM ( SELECT [id] FROM [table] ORDER BY [id] DESC LIMIT N ) AS Temp ) AS Temp2 ON [table].[id] = [Temp2].[id]
источник
DELETE FROM table WHERE id NOT IN ( SELECT id FROM table ORDER BY id, desc LIMIT 0, 10 )
источник
Почему нет
DELETE FROM table ORDER BY id DESC LIMIT 1, 123456789
Просто удалите все, кроме первой строки (порядок DESC!), Используя очень-очень большое число в качестве второго LIMIT-аргумента. Глянь сюда
источник
DELETE
не поддерживает[offset],
илиOFFSET
: dev.mysql.com/doc/refman/5.0/en/delete.htmlОтвечая на это через долгое время ... Я столкнулся с той же ситуацией, и вместо того, чтобы использовать упомянутые ответы, я пришел с ниже -
DELETE FROM table_name order by ID limit 10
Это приведет к удалению первых 10 записей и сохранению последних записей.
источник