No puede eliminar los registros de esa manera, el problema principal es que no puede usar una subconsulta para especificar el valor de una cláusula LIMIT.
Esto funciona (probado en 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
);
La subconsulta intermedio se requiere. Sin él, nos encontraríamos con dos errores:
- Error de SQL (1093): no puede especificar la tabla de destino 'tabla' para la actualización en la cláusula FROM ; MySQL no le permite consultar la tabla que está eliminando desde una subconsulta directa.
- Error de SQL (1235): esta versión de MySQL aún no admite la subconsulta 'LIMIT & IN / ALL / ANY / SOME' . No puede usar la cláusula LIMIT dentro de una subconsulta directa de un operador NOT IN.
Afortunadamente, el uso de una subconsulta intermedia nos permite evitar estas dos limitaciones.
Nicole ha señalado que esta consulta se puede optimizar significativamente para ciertos casos de uso (como este). Recomiendo leer esa respuesta también para ver si se ajusta a la suya.
Sé que estoy resucitando una pregunta bastante antigua, pero recientemente me encontré con este problema, pero necesitaba algo que se adapte bien a grandes números . No había datos de rendimiento existentes, y dado que esta pregunta ha recibido bastante atención, pensé en publicar lo que encontré.
Las soluciones que realmente funcionaron fueron el método /
NOT IN
subconsulta doble de Alex Barrett (similar al de Bill Karwin ) y elLEFT JOIN
método de Quassnoi .Desafortunadamente, los dos métodos anteriores crean tablas temporales intermedias muy grandes y el rendimiento se degrada rápidamente a medida que aumenta la cantidad de registros que no se eliminan.
Lo que me decidí utiliza la doble subconsulta de Alex Barrett (¡gracias!) Pero usa en
<=
lugar deNOT 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 )
Se utiliza
OFFSET
para obtener la identificación del registro N y elimina ese registro y todos los registros anteriores.Dado que ordenar ya es una suposición de este problema (
ORDER BY id DESC
),<=
es un ajuste perfecto.Es mucho más rápido, ya que la tabla temporal generada por la subconsulta contiene solo un registro en lugar de N registros.
Caso de prueba
Probé los tres métodos de trabajo y el nuevo método anterior en dos casos de prueba.
Ambos casos de prueba usan 10000 filas existentes, mientras que la primera prueba conserva 9000 (elimina las 1000 más antiguas) y la segunda prueba 50 (elimina las 9950 más antiguas).
+-----------+------------------------+----------------------+ | | 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 | +-----------+------------------------+----------------------+
Lo interesante es que el
<=
método ve un mejor rendimiento en todos los ámbitos, pero en realidad mejora cuanto más se conserva, en lugar de peor.fuente
ROW_NUMBER()
: stackoverflow.com/questions/603724/…Desafortunadamente, para todas las respuestas dadas por otras personas, no puede
DELETE
ySELECT
de una tabla determinada en la misma consulta.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 tampoco puede admitir
LIMIT
en una subconsulta. Estas son las limitaciones de 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'
La mejor respuesta que se me ocurre es hacer esto en dos etapas:
SELECT id FROM mytable ORDER BY id DESC LIMIT n;
Recoge los ID y conviértelos en una cadena separada por comas:
DELETE FROM mytable WHERE id NOT IN ( ...comma-separated string... );
(Normalmente, la interpolación de una lista separada por comas en una declaración SQL introduce cierto riesgo de inyección SQL, pero en este caso los valores no provienen de una fuente no confiable, se sabe que son valores enteros de la propia base de datos).
nota: aunque esto no hace el trabajo en una sola consulta, a veces una solución más simple y práctica es la más efectiva.
fuente
DELETE FROM mytable WHERE id NOT IN (SELECT id FROM mytable ORDER BY id DESC LIMIT 3);
funciona bien.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
fuente
Si su identificación es incremental, use algo como
delete from table where id < (select max(id) from table)-N
fuente
Para eliminar todos los registros excepto el último N , puede utilizar la consulta que se indica a continuación.
Es una consulta única pero con muchas declaraciones, por lo que en realidad no es una consulta única como se pretendía en la pregunta original.
También necesita una variable y una declaración preparada incorporada (en la consulta) debido a un error en MySQL.
Espero que pueda ser útil de todos modos ...
nnn son las filas que debe conservar y la Tabla es la tabla en la que está trabajando.
Supongo que tiene un registro de autoincremento llamado 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;
Lo bueno de este enfoque es el rendimiento : he probado la consulta en una base de datos local con aproximadamente 13.000 registros, manteniendo los últimos 1.000. Funciona en 0,08 segundos.
El guión de la respuesta aceptada ...
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 );
Tarda 0,55 segundos. Aproximadamente 7 veces más.
Entorno de prueba: mySQL 5.5.25 en una MacBookPro i7 de finales de 2011 con SSD
fuente
DELETE FROM table WHERE ID NOT IN (SELECT MAX(ID) ID FROM table)
fuente
prueba a continuación la consulta:
DELETE FROM tablename WHERE id < (SELECT * FROM (SELECT (MAX(id)-10) FROM tablename ) AS a)
la subconsulta interna devolverá el valor de los 10 primeros y la consulta externa eliminará todos los registros excepto los 10 principales.
fuente
Qué pasa :
SELECT * FROM table del LEFT JOIN table keep ON del.id < keep.id GROUP BY del.* HAVING count(*) > N;
Devuelve filas con más de N filas antes. Podría ser útil ?
fuente
Usar id para esta tarea no es una opción en muchos casos. Por ejemplo, tabla con estados de Twitter. Aquí hay una variante con un campo de marca de tiempo especificado.
delete from table where access_time >= ( select access_time from ( select access_time from table order by access_time limit 150000,1 ) foo )
fuente
Solo quería incluir esto en la mezcla para cualquiera que use Microsoft SQL Server en lugar de MySQL. La palabra clave 'Límite' no es compatible con MSSQL, por lo que deberá utilizar una alternativa. Este código funcionó en SQL 2008 y se basa en esta publicación 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)
Es cierto que esto no es elegante. Si puede optimizar esto para Microsoft SQL, comparta su solución. ¡Gracias!
fuente
Si también necesita eliminar los registros basados en alguna otra columna, aquí hay una solución:
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
fuente
Esto debería funcionar también:
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]
fuente
DELETE FROM table WHERE id NOT IN ( SELECT id FROM table ORDER BY id, desc LIMIT 0, 10 )
fuente
Por qué no
DELETE FROM table ORDER BY id DESC LIMIT 1, 123456789
Simplemente elimine todo menos la primera fila (¡el orden es DESC!), Usando un número muy grande como segundo argumento LIMIT. Mira aquí
fuente
DELETE
no admite[offset],
oOFFSET
: dev.mysql.com/doc/refman/5.0/en/delete.htmlRespondiendo esto después de mucho tiempo ... Me encontré con la misma situación y en lugar de usar las respuestas mencionadas, vine a continuación:
DELETE FROM table_name order by ID limit 10
Esto eliminará los primeros 10 registros y mantendrá los últimos registros.
fuente