เป็นไปได้ไหมที่จะสร้างแบบสอบถาม mysql เดียว (โดยไม่มีตัวแปร) เพื่อลบระเบียนทั้งหมดออกจากตารางยกเว้น N ล่าสุด (เรียงตาม id desc)
อะไรทำนองนี้มันใช้ไม่ได้ :)
delete from table order by id ASC limit ((select count(*) from table ) - N)
ขอบคุณ.
เป็นไปได้ไหมที่จะสร้างแบบสอบถาม mysql เดียว (โดยไม่มีตัวแปร) เพื่อลบระเบียนทั้งหมดออกจากตารางยกเว้น N ล่าสุด (เรียงตาม id desc)
อะไรทำนองนี้มันใช้ไม่ได้ :)
delete from table order by id ASC limit ((select count(*) from table ) - N)
ขอบคุณ.
คำตอบ:
คุณไม่สามารถลบเรกคอร์ดด้วยวิธีนั้นปัญหาหลักคือคุณไม่สามารถใช้คิวรีย่อยเพื่อระบุค่าของประโยค 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 -- keep this many records
) foo
);
จำเป็นต้องมีการสืบค้นย่อยระดับกลาง หากไม่มีเราจะพบข้อผิดพลาดสองประการ:
โชคดีที่การใช้แบบสอบถามย่อยระดับกลางช่วยให้เราสามารถข้ามข้อ จำกัด ทั้งสองนี้ได้
Nicole ได้ชี้ให้เห็นว่าข้อความค้นหานี้สามารถปรับให้เหมาะสมได้อย่างมีนัยสำคัญสำหรับกรณีการใช้งานบางกรณี (เช่นกรณีนี้) ฉันขอแนะนำให้อ่านคำตอบนั้นด้วยเพื่อดูว่าเหมาะกับคุณหรือไม่
ฉันรู้ว่าฉันกำลังรื้อฟื้นคำถามที่ค่อนข้างเก่า แต่เมื่อเร็ว ๆ นี้ฉันพบปัญหานี้ แต่ต้องการบางสิ่งที่ปรับขนาดเป็นจำนวนมากได้ดีเครื่องชั่งน้ำหนักจำนวนมากได้ดีไม่มีข้อมูลประสิทธิภาพใด ๆ เลยและเนื่องจากคำถามนี้ได้รับความสนใจไม่น้อยฉันจึงคิดว่าจะโพสต์สิ่งที่พบ
โซลูชันที่ใช้งานได้จริงคือแบบสอบถาม /NOT IN
วิธีการย่อยคู่ของ Alex Barrett (คล้ายกับBill Karwin's ) และวิธีการของ QuassnoiLEFT JOIN
น่าเสียดายที่ทั้งสองวิธีข้างต้นสร้างตารางชั่วคราวระดับกลางที่มีขนาดใหญ่มากและประสิทธิภาพจะลดลงอย่างรวดเร็วเนื่องจากจำนวนระเบียนที่ไม่ถูกลบมีจำนวนมาก
สิ่งที่ฉันตัดสินใช้แบบสอบถามย่อยคู่ของ Alex Barrett (ขอบคุณ!) แต่ใช้<=
แทน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
เพื่อรับ id ของระเบียนNและลบบันทึกนั้นและระเบียนก่อนหน้าทั้งหมด
เนื่องจากการสั่งซื้อเป็นข้อสันนิษฐานของปัญหานี้อยู่แล้ว ( ORDER BY id DESC
) <=
จึงเหมาะสมอย่างยิ่ง
เร็วกว่ามากเนื่องจากตารางชั่วคราวที่สร้างโดยแบบสอบถามย่อยมีเพียงระเบียนเดียวแทนที่จะเป็นNระเบียน
ฉันทดสอบวิธีการทำงานทั้งสามวิธีและวิธีการใหม่ข้างต้นในสองกรณีการทดสอบ
กรณีทดสอบทั้งสองใช้ 10,000 แถวที่มีอยู่ในขณะที่การทดสอบครั้งแรกเก็บ 9000 (ลบ 1,000 ที่เก่าที่สุด) และการทดสอบครั้งที่สองเก็บ 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
ในการลบบันทึกทั้งหมดยกเว้น te last Nคุณสามารถใช้แบบสอบถามที่รายงานด้านล่าง
เป็นแบบสอบถามเดียว แต่มีข้อความจำนวนมากดังนั้นจึงไม่ใช่แบบสอบถามเดียวอย่างที่ตั้งใจไว้ในคำถามเดิม
นอกจากนี้คุณต้องมีตัวแปรและคำสั่งที่เตรียมไว้ในตัว (ในแบบสอบถาม) เนื่องจากข้อบกพร่องใน MySQL
หวังว่ามันอาจจะมีประโยชน์ต่อไป ...
nnnคือแถวที่ต้องเก็บและตารางคือตารางที่คุณกำลังทำงานอยู่
ฉันสมมติว่าคุณมีบันทึกการสร้างอัตโนมัติชื่อ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 รายการโดยเก็บ 1,000 รายการล่าสุดไว้ มันทำงานใน 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 ใน MacBookPro i7 รุ่นปลายปี 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 สำหรับงานนี้ไม่ใช่ทางเลือกในหลาย ๆ กรณี ตัวอย่างเช่น - ตารางที่มีสถานะ Twitter นี่คือตัวแปรที่มีช่องการประทับเวลาที่ระบุ
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 MSSQL ไม่รองรับคีย์เวิร์ด 'Limit' ดังนั้นคุณจะต้องใช้ทางเลือกอื่น รหัสนี้ทำงานใน 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_name order by ID limit 10
การดำเนินการนี้จะลบระเบียน 10 อันดับแรกและเก็บบันทึกล่าสุดไว้