Anda tidak dapat menghapus record dengan cara itu, masalah utamanya adalah Anda tidak dapat menggunakan subquery untuk menentukan nilai klausa LIMIT.
Ini berfungsi (diuji di 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
);
Subquery menengah yang diperlukan. Tanpa itu kami akan mengalami dua kesalahan:
- Kesalahan SQL (1093): Anda tidak dapat menentukan tabel target 'tabel' untuk pembaruan di klausa FROM - MySQL tidak mengizinkan Anda untuk merujuk ke tabel yang Anda hapus dari dalam subkueri langsung.
- Kesalahan SQL (1235): Versi MySQL ini belum mendukung subkueri 'LIMIT & IN / ALL / ANY / SOME' - Anda tidak dapat menggunakan klausa LIMIT dalam subkueri langsung dari operator NOT IN.
Untungnya, menggunakan subkueri perantara memungkinkan kita melewati kedua batasan ini.
Nicole telah menunjukkan bahwa kueri ini dapat dioptimalkan secara signifikan untuk kasus penggunaan tertentu (seperti yang satu ini). Saya merekomendasikan membaca jawaban itu juga untuk melihat apakah itu cocok dengan Anda.
Saya tahu saya membangkitkan pertanyaan yang cukup lama, tetapi saya baru-baru ini mengalami masalah ini, tetapi membutuhkan sesuatu yang berskala besar dengan baik . Tidak ada data kinerja yang ada, dan karena pertanyaan ini mendapat sedikit perhatian, saya pikir saya akan memposting apa yang saya temukan.
Solusi yang benar-benar berhasil adalah sub-query /
NOT IN
metode ganda Alex Barrett (mirip dengan Bill Karwin ), dan metode QuassnoiLEFT JOIN
.Sayangnya, kedua metode di atas membuat tabel sementara menengah yang sangat besar dan kinerja menurun dengan cepat karena jumlah record yang tidak dihapus menjadi besar.
Apa yang saya putuskan menggunakan sub-kueri ganda Alex Barrett (terima kasih!) Tetapi menggunakan
<=
alih-alihNOT 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 )
Ini digunakan
OFFSET
untuk mendapatkan id dari record N dan menghapus record itu dan semua record sebelumnya.Karena pengurutan sudah merupakan asumsi dari masalah ini (
ORDER BY id DESC
),<=
sangat cocok.Ini jauh lebih cepat, karena tabel sementara yang dibuat oleh subkueri hanya berisi satu catatan, bukan catatan N.
Kasus cobaan
Saya menguji tiga metode kerja dan metode baru di atas dalam dua kasus pengujian.
Kedua kasus pengujian menggunakan 10.000 baris yang ada, sedangkan pengujian pertama menyimpan 9000 (menghapus 1000 terlama) dan pengujian kedua menyimpan 50 (menghapus 9950 terlama).
+-----------+------------------------+----------------------+ | | 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 | +-----------+------------------------+----------------------+
Yang menarik adalah bahwa
<=
metode ini melihat kinerja yang lebih baik secara keseluruhan, tetapi sebenarnya semakin baik semakin banyak Anda simpan, bukan semakin buruk.sumber
ROW_NUMBER()
: stackoverflow.com/questions/603724/…Sayangnya untuk semua jawaban yang diberikan oleh orang lain, Anda tidak bisa
DELETE
danSELECT
dari tabel tertentu dalam kueri yang sama.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 juga tidak dapat mendukung
LIMIT
subquery. Ini adalah batasan 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'
Jawaban terbaik yang dapat saya berikan adalah melakukan ini dalam dua tahap:
SELECT id FROM mytable ORDER BY id DESC LIMIT n;
Kumpulkan id dan buat menjadi string yang dipisahkan koma:
DELETE FROM mytable WHERE id NOT IN ( ...comma-separated string... );
(Biasanya menginterpolasi daftar yang dipisahkan koma ke dalam pernyataan SQL memperkenalkan beberapa risiko injeksi SQL, tetapi dalam kasus ini nilai tidak berasal dari sumber yang tidak tepercaya, nilai tersebut dikenal sebagai nilai integer dari database itu sendiri.)
catatan: Meskipun ini tidak menyelesaikan pekerjaan dalam satu kueri, terkadang solusi yang lebih sederhana dan selesaikan adalah yang paling efektif.
sumber
DELETE FROM mytable WHERE id NOT IN (SELECT id FROM mytable ORDER BY id DESC LIMIT 3);
berfungsi dengan baik.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
sumber
Jika id Anda bertambah, gunakan sesuatu seperti
delete from table where id < (select max(id) from table)-N
sumber
Untuk menghapus semua record kecuali N terakhir Anda dapat menggunakan query yang dilaporkan di bawah ini.
Ini adalah kueri tunggal tetapi dengan banyak pernyataan jadi sebenarnya ini bukan kueri tunggal seperti yang dimaksudkan dalam pertanyaan asli.
Anda juga memerlukan variabel dan pernyataan siap pakai (dalam kueri) karena ada bug di MySQL.
Semoga bermanfaat pula ...
nnn adalah baris yang harus disimpan dan Tabel adalah tabel yang sedang Anda kerjakan.
Saya berasumsi Anda memiliki data autoincrementing bernama 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;
Hal yang baik tentang pendekatan ini adalah kinerja : Saya telah menguji kueri pada DB lokal dengan sekitar 13.000 catatan, menyimpan 1.000 yang terakhir. Ini berjalan dalam 0,08 detik.
Naskah dari jawaban yang diterima ...
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 );
Butuh 0,55 detik. Sekitar 7 kali lebih banyak.
Lingkungan pengujian: mySQL 5.5.25 pada MacBookPro i7 2011 akhir dengan SSD
sumber
DELETE FROM table WHERE ID NOT IN (SELECT MAX(ID) ID FROM table)
sumber
coba kueri di bawah ini:
DELETE FROM tablename WHERE id < (SELECT * FROM (SELECT (MAX(id)-10) FROM tablename ) AS a)
kueri bagian dalam akan mengembalikan nilai 10 teratas dan kueri luar akan menghapus semua rekaman kecuali 10 teratas.
sumber
Bagaimana dengan :
SELECT * FROM table del LEFT JOIN table keep ON del.id < keep.id GROUP BY del.* HAVING count(*) > N;
Ini mengembalikan baris dengan lebih dari N baris sebelumnya. Bisa bermanfaat?
sumber
Menggunakan id untuk tugas ini bukanlah pilihan dalam banyak kasus. Misalnya - tabel dengan status twitter. Berikut adalah varian dengan kolom stempel waktu yang ditentukan.
delete from table where access_time >= ( select access_time from ( select access_time from table order by access_time limit 150000,1 ) foo )
sumber
Hanya ingin memasukkan ini ke dalam campuran untuk siapa saja yang menggunakan Microsoft SQL Server, bukan MySQL. Kata kunci 'Limit' tidak didukung oleh MSSQL, jadi Anda harus menggunakan alternatif. Kode ini bekerja di SQL 2008, dan didasarkan pada posting SO ini. 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)
Memang, ini tidak elegan. Jika Anda dapat mengoptimalkan ini untuk Microsoft SQL, silakan bagikan solusi Anda. Terima kasih!
sumber
Jika Anda perlu menghapus catatan berdasarkan beberapa kolom lain juga, berikut solusinya:
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
sumber
Ini seharusnya bekerja dengan baik:
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]
sumber
DELETE FROM table WHERE id NOT IN ( SELECT id FROM table ORDER BY id, desc LIMIT 0, 10 )
sumber
Kenapa tidak
DELETE FROM table ORDER BY id DESC LIMIT 1, 123456789
Hapus saja semua kecuali baris pertama (urutannya adalah DESC!), Menggunakan jumlah yang sangat besar sebagai argumen LIMIT kedua. Lihat disini
sumber
DELETE
tidak mendukung[offset],
atauOFFSET
: dev.mysql.com/doc/refman/5.0/en/delete.htmlMenjawab ini setelah waktu yang lama ... Datang ke situasi yang sama dan alih-alih menggunakan jawaban yang disebutkan, saya datang dengan di bawah -
DELETE FROM table_name order by ID limit 10
Ini akan menghapus 10 catatan pertama dan menyimpan catatan terbaru.
sumber