SQL क्वेरी: नवीनतम N को छोड़कर तालिका से सभी रिकॉर्ड हटाएं?


90

क्या तालिका से सभी रिकॉर्ड को हटाने के लिए एक एकल mysql क्वेरी (बिना चरों के) का निर्माण संभव है, नवीनतम N (id desc द्वारा क्रमबद्ध) को छोड़कर?

कुछ इस तरह से, केवल यह काम नहीं करता है :)

delete from table order by id ASC limit ((select count(*) from table ) - N)

धन्यवाद।

जवाबों:


139

आप इस तरह से रिकॉर्ड नहीं हटा सकते हैं, मुख्य मुद्दा यह है कि आप एक उप-समूह का उपयोग नहीं कर सकते हैं एक लिमाट क्लॉज का मान निर्दिष्ट करने के लिए।

यह काम करता है (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
);

मध्यवर्ती उपकुंजी की आवश्यकता है। इसके बिना हम दो त्रुटियों में भाग लेंगे:

  1. SQL त्रुटि (1093): आप FROM क्लॉज में अपडेट के लिए लक्ष्य तालिका 'तालिका' को निर्दिष्ट नहीं कर सकते - MySQL आपको उस तालिका को संदर्भित करने की अनुमति नहीं देता है जिसे आप एक प्रत्यक्ष उपकुंजी के भीतर से हटा रहे हैं।
  2. SQL त्रुटि (1235): MySQL का यह संस्करण अभी तक 'LIMIT & IN / ALL / Any / SOME सबक्वेरी' का समर्थन नहीं करता है - आप NOTIT ऑपरेटर के प्रत्यक्ष उप-वर्ग के भीतर LIMIT खंड का उपयोग नहीं कर सकते।

सौभाग्य से, एक मध्यवर्ती उपकुंजी का उपयोग करके हमें इन दोनों सीमाओं को बायपास करने की अनुमति मिलती है।


निकोल ने कहा है कि इस क्वेरी को कुछ उपयोग मामलों (जैसे यह एक) के लिए महत्वपूर्ण रूप से अनुकूलित किया जा सकता है। मैं उस उत्तर को पढ़ने के साथ-साथ यह भी देखने की सलाह देता हूं कि क्या यह आपके योग्य है।


4
ठीक है कि काम करता है - लेकिन मेरे लिए, यह उस तरह से रहस्यमयी चाल का सहारा लेने के लिए अयोग्य और असंतोषजनक है। फिर भी उत्तर के लिए +1।
बिल कार्वेन

1
मैं इसे एक स्वीकृत उत्तर के रूप में चिह्नित करता हूं, क्योंकि यह वही करता है जो मैंने पूछा था। लेकिन मैं व्यक्तिगत रूप से इसे सरल रखने के लिए दो प्रश्नों में शायद करूंगा :) मुझे लगा कि शायद कुछ त्वरित और आसान तरीका था।
23:00

1
धन्यवाद एलेक्स, आपके जवाब ने मेरी मदद की। मैं देखता हूं कि मध्यवर्ती उपश्रेणी की आवश्यकता है, लेकिन मुझे समझ नहीं आता कि क्यों। क्या आपके पास इसके लिए स्पष्टीकरण है?
Sv1

8
एक सवाल: "फू" किसके लिए है?
सेबेस्टियन ब्रेइट

9
पेरोलोको, मैंने बिना फू के कोशिश की और यह त्रुटि मिली: ERROR 1248 (42000): प्रत्येक व्युत्पन्न तालिका का अपना उपनाम होना चाहिए, इसलिए हमारा उत्तर, प्रत्येक व्युत्पन्न तालिका का अपना उपनाम होना चाहिए!
codgman

106

मुझे पता है कि मैं काफी पुराने सवाल को फिर से जीवित कर रहा हूं, लेकिन मैं हाल ही में इस मुद्दे पर भाग गया, लेकिन कुछ ऐसी चीजों की जरूरत थी जो बड़ी संख्या में अच्छी हो । कोई भी मौजूदा प्रदर्शन डेटा नहीं था, और चूंकि इस सवाल पर बहुत अधिक ध्यान दिया गया था, मैंने सोचा कि मुझे जो मिला है वह पोस्ट करूँगा।

वास्तव में काम करने वाले समाधान एलेक्स बैरेट की डबल उप-क्वेरी /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की आईडी प्राप्त करने के लिए उपयोग करता है और उस रिकॉर्ड और पिछले सभी रिकॉर्ड को हटा देता है।

चूंकि ऑर्डर करना पहले से ही इस समस्या ( ORDER BY id DESC) की धारणा है , <=एक सही फिट है।

यह बहुत तेज है, क्योंकि उप-वर्ग द्वारा बनाई गई अस्थायी तालिका में एन रिकॉर्ड के बजाय सिर्फ एक रिकॉर्ड है।

परीक्षण का मामला

मैंने तीन कार्य विधियों और नई विधि का परीक्षण दो परीक्षण मामलों में किया।

दोनों परीक्षण मामले 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 |
+-----------+------------------------+----------------------+

मजे की बात यह है कि <=विधि पूरे बोर्ड में बेहतर प्रदर्शन देखती है, लेकिन वास्तव में आप जितना बेहतर रखते हैं, उतना खराब होने के बजाय बेहतर होता है।


11
मैं इस धागे को 4.5 साल बाद फिर से पढ़ रहा हूं। इसके अलावा अच्छा!
एलेक्स बैरेट

वाह, यह बहुत अच्छा लग रहा है, लेकिन Microsoft SQL 2008 में काम नहीं करता है। मुझे यह संदेश मिलता है: "'सीमा' के पास गलत सिंटैक्स। यह अच्छा है कि यह MySQL में काम करता है, लेकिन मुझे वैकल्पिक समाधान खोजने की आवश्यकता होगी।
केन पामर

1
@KenPalmer आपको अभी भी एक विशिष्ट पंक्ति का उपयोग करने में सक्षम होना चाहिए ROW_NUMBER(): stackoverflow.com/questions/603724/…
निकोल

3
SQL और mySQL के बीच स्विच करते समय @KenPalmer LIMIT के बजाय SELECT TOP का उपयोग करें
अल्फा G33k

1
उसके लिए चीयर्स। इसने मेरे (बहुत बड़े) डेटा पर क्वेरी को 12 मिनट से 3.64 सेकंड तक कम कर दिया!
लेउवे

10

दुर्भाग्य से सभी अन्य लोगों द्वारा दिए गए, आप नहीं कर सकते जवाब के लिए 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

न ही LIMITएक सबक्वेरी में MySQL का समर्थन किया जा सकता है । ये 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 इंजेक्शन का कुछ जोखिम होता है, लेकिन इस मामले में मान किसी अविश्वसनीय स्रोत से नहीं आ रहे हैं, उन्हें डेटाबेस से पूर्णांक मान के रूप में जाना जाता है।)

ध्यान दें: हालांकि यह एक ही क्वेरी में काम नहीं करता है , कभी-कभी अधिक सरल, गेट-इट-किया समाधान सबसे प्रभावी होता है।


लेकिन आप एक डिलीट और सिलेक्ट के बीच इनर जॉइन कर सकते हैं। नीचे मैंने जो किया वह काम करना चाहिए।
achinda99

उपमहाद्वीप में काम करने के लिए आपको एक मध्यस्थ उपकेंद्र का उपयोग करना होगा।
एलेक्स बैरेट

@ achinda99: मैं इस धागे पर आपसे कोई जवाब नहीं देख रहा हूँ ...?
बिल कार्वन

मुझे एक बैठक के लिए खींच लिया गया। मेरी गलती। मेरे पास लिखित परीक्षा का परीक्षण करने के लिए अभी मेरे पास एक परीक्षण का माहौल नहीं है, लेकिन मैंने एलेक्स बैरेट का क्या किया है और मैंने इसे एक आंतरिक जुड़ाव के साथ काम करने के लिए प्राप्त किया है।
achinda99

यह MySQL की एक बेवकूफ सीमा है। PostgreSQL के साथ, DELETE FROM mytable WHERE id NOT IN (SELECT id FROM mytable ORDER BY id DESC LIMIT 3);ठीक काम करता है।
bortzmeyer

8
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

5

अगर आपकी id वृद्धिशील है तो कुछ का उपयोग करें

delete from table where id < (select max(id) from table)-N

2
इस अच्छी चाल में एक बड़ी समस्या: धारावाहिक हमेशा सन्निहित नहीं होते हैं (उदाहरण के लिए जब रोलबैक थे)।
bortzmeyer

5

ते अंतिम एन को छोड़कर सभी रिकॉर्ड को हटाने के लिए आप नीचे दी गई क्वेरी का उपयोग कर सकते हैं।

यह एक एकल क्वेरी है लेकिन कई कथनों के साथ इसलिए यह वास्तव में एक एकल क्वेरी नहीं है जिस तरह से यह मूल प्रश्न में था।

इसके अलावा आपको MySQL में बग के कारण एक वैरिएबल और बिल्ट-इन (क्वेरी में) तैयार स्टेटमेंट की जरूरत है।

आशा है कि यह वैसे भी उपयोगी हो सकता है ...

NNN पंक्तियों के लिए कर रहे रखना और theTable तालिका आप पर काम कर रहे है।

मैं मान रहा हूं कि आपके पास आईडी नाम का एक ऑटोइन्क्रिमेंट रिकॉर्ड है

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;

इस दृष्टिकोण के बारे में अच्छी बात यह है कि प्रदर्शन : मैंने एक स्थानीय DB पर क्वेरी का परीक्षण लगभग 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 गुना अधिक।

परीक्षण वातावरण: SSD के साथ 2011 के अंत में i7 मैकबुकप्रो पर mySQL 5.5.25



1

क्वेरी से नीचे का प्रयास करें:

DELETE FROM tablename WHERE id < (SELECT * FROM (SELECT (MAX(id)-10) FROM tablename ) AS a)

आंतरिक उप क्वेरी शीर्ष 10 मान लौटाएगी और बाहरी क्वेरी शीर्ष 10 को छोड़कर सभी रिकॉर्ड हटा देगी।


1
इस उत्तर में आने वाले लोगों के लिए यह काम कैसे फायदेमंद होगा, इसके बारे में कुछ व्याख्या। आमतौर पर कोड डंपिंग की सिफारिश नहीं की जाती है।
रेय्यरेंग

यह सुसंगत आईडी के साथ सही नहीं है
स्लाव रोझनेव

0

व्हाट अबाउट :

SELECT * FROM table del 
         LEFT JOIN table keep
         ON del.id < keep.id
         GROUP BY del.* HAVING count(*) > N;

यह पहले से अधिक N पंक्तियों वाली पंक्तियाँ लौटाता है। उपयोगी हो सकता है?


0

इस कार्य के लिए आईडी का उपयोग करना कई मामलों में एक विकल्प नहीं है। उदाहरण के लिए - ट्विटर स्टेटस वाली तालिका। यहाँ निर्दिष्ट टाइमस्टैम्प क्षेत्र के साथ एक संस्करण है।

delete from table 
where access_time >= 
(
    select access_time from  
    (
        select access_time from table 
            order by access_time limit 150000,1
    ) foo    
)

0

बस MySQL के बजाय Microsoft SQL Server का उपयोग करने वाले किसी के लिए भी इस मिश्रण में फेंकना चाहता था। 'सीमा' शब्द 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 के लिए अनुकूलित करने में सक्षम हैं, तो कृपया अपना समाधान साझा करें। धन्यवाद!


0

यदि आपको कुछ अन्य कॉलम के आधार पर रिकॉर्ड को हटाने की आवश्यकता है, तो यहां एक समाधान है:

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

0

यह भी काम करना चाहिए:

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]


-1

क्यों नहीं

DELETE FROM table ORDER BY id DESC LIMIT 1, 123456789

बस सभी को हटा दें लेकिन दूसरी लिमिट-तर्क के रूप में एक बहुत बड़े संख्या का उपयोग करके पहली पंक्ति (ऑर्डर DESC है!)। यहाँ देखें


2
DELETEसमर्थन नहीं करता है [offset],या OFFSET: dev.mysql.com/doc/refman/5.0/en/delete.html
निकोल

-1

लंबे समय के बाद इसका उत्तर देना ... उसी स्थिति में आया और वर्णित उत्तरों का उपयोग करने के बजाय, मैं नीचे आया -

DELETE FROM table_name order by ID limit 10

यह 1 10 रिकॉर्ड हटा देगा और नवीनतम रिकॉर्ड रखेगा।


सवाल पूछा गया "सभी अंतिम एन रिकॉर्ड्स" और "एक ही प्रश्न में"। लेकिन ऐसा लगता है कि आपको अभी भी तालिका में सभी रिकॉर्डों को गिनने के लिए एक पहली क्वेरी की आवश्यकता है - कुल - एन
पाओलो

@Paolo हमें सभी रिकॉर्डों को गिनने के लिए एक क्वेरी की आवश्यकता नहीं है क्योंकि उपरोक्त क्वेरी पिछले 10 रिकॉर्डों को छोड़कर सभी को हटा देती है।
नितेश

1
नहीं, वह क्वेरी 10 सबसे पुराने रिकॉर्ड को हटा देती है। ओपी एन सबसे हाल के रिकॉर्ड को छोड़कर सब कुछ हटाना चाहता है। तुम्हारा एक बुनियादी समाधान है जिसे एक गिनती क्वेरी के साथ जोड़ा जाएगा, जबकि ओपी पूछ रहा है कि क्या एक ही क्वेरी में सब कुछ संयोजित करने का एक तरीका है।
क्रिसमॉल

@ क्रिसमोल मैं सहमत हूँ। क्या मैं अब इस उत्तर को संपादित / हटा दूंगा ताकि उपयोगकर्ता मुझे वोट न दें या इसे छोड़ दें?
नितेश
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.