बिना लॉग इन के SQL में टेबल के बड़े डेटा को कैसे डिलीट करें?


128

मेरे पास एक बड़ी डेटा टेबल है। इस तालिका में 10 मिलियन रिकॉर्ड हैं।

इस क्वेरी के लिए सबसे अच्छा तरीका क्या है

   Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())

4
:) मुझे डर है जब तक कि आप सभी पंक्तियों को पुनः प्राप्त करने के लिए किसी प्रकार की ईटीएल लिखने के लिए तैयार नहीं हैं, तब तक एक और तालिका में सभी पंक्तियों को बार-बार प्राप्त करें , आप इसे लिखने से रोकने में सक्षम नहीं होंगे
TMNT2014

लॉगिंग एक लचीला लेनदेन होने का एक या कुछ भी नहीं कार्य है। इसका शाब्दिक अर्थ यह नहीं है कि कुछ ऑपरेशन के लिए लॉग नहीं है, लेकिन दूसरों के लिए नहीं है, अन्यथा लॉग बेकार है।
एरिक फिलिप्स

1
डेटा आप रखना तालिका काटना चाहते निर्यात, और पुन: आयात
बोहेमियन

एक अन्य विकल्प एक सारणी का उपयोग करना होगा जो लॉग नहीं हैं। इसलिए अपने readTime> = dateadd (MONTH; -7, GETDATE ()) डेटा को एक टेबल वेरिएबल में स्टोर करें और फिर ऑरिजनल टेबल को ट्रेंकुलेट करें और टेबल वेरिएबल से डेटा को वापस कॉपी करें। हालाँकि मैं कुछ गलत होने की स्थिति में डेटा का बैक अप रखूंगा और टेबल अनजाने में ट्रंक हो जाता है। :) और हमेशा अपनी स्क्रिप्ट का परीक्षण कम वातावरण पर करें।
TMNT2014

जवाबों:


203
  1. यदि आप उस तालिका की सभी पंक्तियों को हटा रहे हैं, तो सबसे सरल विकल्प ट्रंकट तालिका है, कुछ ऐसा है

    TRUNCATE TABLE LargeTable
    GO
    

    Truncate तालिका बस तालिका को खाली कर देगी, आप हटाए जा रहे पंक्तियों को सीमित करने के लिए WHERE क्लॉज का उपयोग नहीं कर सकते हैं और कोई ट्रिगर नहीं निकाल दिया जाएगा।

  2. दूसरी ओर यदि आप 80-90 प्रतिशत से अधिक डेटा डिलीट कर रहे हैं, तो कहें कि यदि आपके पास कुल 11 मिलियन पंक्तियाँ हैं और आप 10 मिलियन हटाना चाहते हैं, तो यह होगा कि आप इन 1 मिलियन पंक्तियों को सम्मिलित करें (जो रिकॉर्ड आप रखना चाहते हैं ) एक और स्टेजिंग टेबल पर। इस बड़ी तालिका को काटें और इन 1 मिलियन पंक्तियों को वापस डालें।

  3. या यदि अनुमतियाँ / दृश्य या अन्य ऑब्जेक्ट्स जिसमें यह बड़ी तालिका है क्योंकि उनकी अंतर्निहित तालिका इस तालिका को छोड़ने से प्रभावित नहीं होती है तो आप इन तालिका में अपेक्षाकृत कम मात्रा में पंक्तियाँ प्राप्त कर सकते हैं और इस तालिका को उसी स्कीमा के साथ दूसरी तालिका बना सकते हैं और इन्हें आयात कर सकते हैं पंक्तियाँ इस पूर्व-बड़ी तालिका में वापस आ गईं।

  4. एक आखिरी विकल्प जो मैं सोच सकता हूं, वह है कि आप अपने डेटाबेस को बदल दें Recovery Mode to SIMPLEऔर फिर कुछ छोटे लूपों का उपयोग करके पंक्तियों को हटा दें।

    DECLARE @Deleted_Rows INT;
    SET @Deleted_Rows = 1;
    
    
    WHILE (@Deleted_Rows > 0)
      BEGIN
       -- Delete some small number of rows at a time
         DELETE TOP (10000)  LargeTable 
         WHERE readTime < dateadd(MONTH,-7,GETDATE())
    
      SET @Deleted_Rows = @@ROWCOUNT;
    END
    

और पुनर्प्राप्ति मोड को पूर्ण में बदलना न भूलें और मुझे लगता है कि आपको इसे पूर्ण रूप से प्रभावी (परिवर्तन या पुनर्प्राप्ति) बनाने के लिए बैकअप लेना होगा।


14
यह भी याद रखें कि यदि आप किसी तालिका को काटते हैं, तो आपके पास इसके साथ कोई भी FKs नहीं हो सकता है।
HLGEM

1
लेकिन यह कैसे सुनिश्चित करें कि आप 80-90% डेटा हटा रहे हैं? मान लेते हैं कि मेरे पास केवल मूल्यों की सीमा है जिन्हें हटा दिया जाना चाहिए। और मेरे पास कुछ टेबल हैं। इसलिए मुझे उनमें से प्रत्येक की जांच करनी है और प्रतिशत की गणना करनी है, और यदि यह लगभग 30% है तो मुझे लगता है कि यह विधि बहुत प्रभावी नहीं है ... मैं अज्ञात मामले के लिए इष्टतम समाधान खोजने की कोशिश कर रहा हूं।
Archont

7
@Archont optimal solution for unknown caseयह सपना नहीं है? दुर्भाग्य से आप हर बीमारी को किसी एक गोली से ठीक नहीं कर सकते हैं; मैंने विभिन्न परिदृश्यों के लिए कुछ संभावित समाधान सुझाए हैं। दुर्भाग्य से यहां कोई स्लिवर बुलेट नहीं है।
15

5
विकल्प 4 चुनने पर ध्यान देने वाली एक बात: तालिका का उपयोग कैसे किया जाता है, इसके आधार पर, लॉक एस्केलेशन से बचने के लिए एक समय में 5000 से कम पंक्तियों को हटाना बेहतर विकल्प हो सकता है ।
डैनियल

यदि हटाने के लिए रिकॉर्ड की संख्या बहुत बड़ी है, तो रिकॉर्ड जो तालिका में बने रहेंगे, मैंने पाया कि रिकॉर्ड के अस्थायी तालिका में सरल चयन जो मूल तालिका में रहेगा और अस्थायी तालिका का नाम बदलना बहुत तेज है। यह देखते हुए कि आप पहचान आईडी विदेशी कुंजी का उपयोग कहीं नहीं करते हैं।
व्लादिमीर बोजिक

96

@ एम-अली का उत्तर सही है लेकिन यह भी ध्यान रखें कि लॉग्स बहुत बढ़ सकते हैं यदि आप प्रत्येक चंक के बाद लेनदेन नहीं करते हैं और एक चेकपॉइंट करते हैं। यह है कि मैं इसे कैसे करूंगा और इस लेख को ले जाऊंगा http://sqlperformance.com/2013/03/io-subsystem/chunk-deletes संदर्भ के रूप में, प्रदर्शन परीक्षण और ग्राफ़ के साथ:

DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;


WHILE (@Deleted_Rows > 0)
  BEGIN

   BEGIN TRANSACTION

   -- Delete some small number of rows at a time
     DELETE TOP (10000)  LargeTable 
     WHERE readTime < dateadd(MONTH,-7,GETDATE())

     SET @Deleted_Rows = @@ROWCOUNT;

   COMMIT TRANSACTION
   CHECKPOINT -- for simple recovery model
END

1
यह उस स्थिति में स्वीकृत उत्तर होना चाहिए जब उपलब्ध डिस्क स्थान सीमित है। बिना COMMIT TRANSACTIONऔर CHECKPOINTलॉग अभी भी बढ़ रहे हैं। इसे स्पष्ट करने के लिए धन्यवाद।
gkoul

+1। बस ध्यान दें कि आप @Deleted_Rows10000 से तुलना करना चाहते हैं या आप अनिश्चित रूप से लूप के साथ समाप्त हो सकते हैं क्योंकि यह अनिश्चित रूप से डेटा के छोटे सेट को हटाना है। इसलिए WHILE (@Deleted_Rows = 10000)- जैसे ही डेटा का एक पूर्ण "पेज" नहीं था उसे हटाने के लिए बंद हो जाएगा। आपके कार्यान्वयन में WHILE (@Deleted_Rows > 0), जबकि लूप फिर से निष्पादित करेगा भले ही यह केवल एक पंक्ति को हटाए, और अगले निष्पादन को हटाने के लिए एक पंक्ति या दो भी मिल सकता है - जिसके परिणामस्वरूप एक अनंत लूप होगा।
NS du Toit

@NSduToit WHERE क्लॉज उन रिकॉर्ड्स पर विचार कर रहा है जो कम से कम 7 महीने पुराने हैं इसलिए नए रिकॉर्ड नहीं होंगे जो उस स्थिति को पूरा करते हैं जब आप डिलीट कर रहे हों।
फ्रांसिस्को गोल्डनस्टीन

@FranciscoGoldenstein खैर, क्वेरी में उपयोग की जाने वाली तारीख प्रत्येक पुनरावृत्ति के साथ अलग होगी क्योंकि आप बार-बार WHILEस्वयं लूप के भीतर की तारीख की गणना करते हैं dateadd(MONTH,-7,GETDATE()):।
NS du Toit

@FranciscoGoldenstein इसके अलावा, शायद यह एक के अलावा अन्य उपयोग के मामलों के लिए - शायद नया डेटा अंतर्निहित तालिका में जोड़ा जाता है जिसके परिणामस्वरूप नए रिकॉर्ड होंगे जो WHILEलूप के विभिन्न पुनरावृत्तियों के बीच हटाए जा सकते हैं ।
NS du Toit

52

आप एक ही क्वेरी को निष्पादित करने के लिए कितनी बार GO + का उपयोग कर सकते हैं।

DELETE TOP (10000)  [TARGETDATABASE].[SCHEMA].[TARGETTABLE] 
WHERE readTime < dateadd(MONTH,-1,GETDATE());
-- how many times you want the query to repeat
GO 100

मुझे यह पसंद है, यह मेरे लिए काम कर रहा है मैंने गलती से एक ही पंक्ति को 26 मिलियन बार एक तालिका में सम्मिलित किया और इसके सभी घटनाओं को हटाने की आवश्यकता थी, जो कि एक एकल डिलीट स्टेटमेंट में सर्वर पर मेमोरी से बाहर भाग गया था, इसलिए यह एक महान प्रश्न है , क्या यह मध्य लूप को रोक देगा यदि यह हटाने के लिए पंक्तियों से बाहर निकलता है?
स्कॉट

2
@ ScottC, यह एक लूप नहीं है, यह सिर्फ क्वेरी (जैसे बैच) को दोहराता है और यदि आप पंक्तियों से बाहर निकलते हैं तो यह कुछ भी नहीं हटा सकता है। लेकिन यह रुकेगा नहीं। अगर आप पंक्तियों को हटाते हैं, तो आपको कुछ मिल जाएगा (0 पंक्ति (ओं) प्रभावित)।
बंकरबस्टर

आह, हाँ मुझे पता चला है कि मेरे प्रश्न को पोस्ट करने के बाद लगभग 5 मिनट, जब से मेरा डिलीट समाप्त हुआ, धन्यवाद यह बहुत मददगार था!
स्कॉट

1
किस MS SQL सर्वर से यह सिंटैक्स GO xxकाम करने वाला है? मुझे एक "संग्रहीत कार्यविधि नहीं मिल सकी" " त्रुटि। GOकमांड के बिना यह ठीक काम करता है।
हाबिल

3
हम्म, ऐसा लगता है कि मैं इसे निष्पादित कर सकता हूं, और यह वास्तव में कई बार चलता है, लेकिन एमएस एसक्यूएल एमजीटी स्टूडियो में यह उल्लेखित त्रुटि के साथ लाल घुंघराले लाइन को दिखाता है (लेकिन फिर एफ 5-रन काम करता है)
एबेल

11

@ फ्रैंसिस्को गोल्डनस्टीन, सिर्फ एक मामूली सुधार। आपके द्वारा चर सेट करने के बाद COMMIT का उपयोग किया जाना चाहिए, अन्यथा WHILE को केवल एक बार निष्पादित किया जाएगा:

DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;

WHILE (@Deleted_Rows > 0)
BEGIN
    BEGIN TRANSACTION

    -- Delete some small number of rows at a time
    DELETE TOP (10000)  LargeTable 
    WHERE readTime < dateadd(MONTH,-7,GETDATE())

    SET @Deleted_Rows = @@ROWCOUNT;

    COMMIT TRANSACTION
    CHECKPOINT -- for simple recovery model

END

10

M.Ali की यह भिन्नता मेरे लिए ठीक काम कर रही है। यह कुछ को हटाता है, लॉग को साफ करता है और दोहराता है। मैं लॉग बढ़ता देख रहा हूँ, छोड़ दो और शुरू करो।

DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
  BEGIN
   -- Delete some small number of rows at a time
    delete top (100000) from InstallLog where DateTime between '2014-12-01' and '2015-02-01'
    SET @Deleted_Rows = @@ROWCOUNT;
    dbcc shrinkfile (MobiControlDB_log,0,truncateonly);
END

यह बहुत उपयोगी था! मैंने इसे संशोधित करने के # of rowsलिए एक समय में हटाने के लिए और WHEREक्लॉज को भी संशोधित किया । एक जादू की तरह काम करता है!
शिव

7

यदि आप विभाजन को लागू करने के लिए तैयार (और सक्षम) हैं, तो यह बहुत ही कम समय के साथ बड़ी मात्रा में डेटा को हटाने के लिए एक प्रभावी तकनीक है। हालांकि एक बार के व्यायाम के लिए लागत प्रभावी नहीं है।


4

मैं मिनटों के मामले में अपनी 21 मिलियन पंक्तियों की तालिका से 19 मिलियन पंक्तियों को हटाने में सक्षम था । यहाँ मेरा दृष्टिकोण है।

यदि आपके पास इस मेज पर एक ऑटो-इन्क्रिमेंटिंग प्राथमिक कुंजी है , तो आप इस प्राथमिक कुंजी का उपयोग कर सकते हैं।

  1. बड़ी तालिका के प्राथमिक मान का न्यूनतम मान प्राप्त करें जहां readTime <dateadd (MONTH; -7, GETDATE ())। (रीड टाइम पर इंडेक्स जोड़ें, यदि पहले से मौजूद नहीं है, तो यह इंडेक्स वैसे भी चरण 3 में तालिका के साथ हटा दिया जाएगा)। इसे एक चर 'min_primary' में संग्रहीत करें

  2. प्राथमिक चरणों> min_primary को मेज़िंग टेबल (स्मृति तालिका में यदि पंक्तियों की संख्या बड़ी नहीं है) वाली सभी पंक्तियों को सम्मिलित करें।

  3. बड़ी टेबल गिरा दो।

  4. तालिका को पुन: बनाएँ। स्टेजिंग टेबल से लेकर मेन टेबल तक सभी पंक्तियों को कॉपी करें।

  5. मंचन तालिका गिराएं।


3

आप कुछ समय के लूप का उपयोग करके छोटे बैचों को हटा सकते हैं, कुछ इस तरह से:

DELETE TOP (10000)  LargeTable 
WHERE readTime < dateadd(MONTH,-7,GETDATE())
WHILE @@ROWCOUNT > 0
BEGIN
    DELETE TOP (10000)  LargeTable 
    WHERE readTime < dateadd(MONTH,-7,GETDATE())
END

2

एक और उपयोग:

SET ROWCOUNT 1000 -- Buffer

DECLARE @DATE AS DATETIME = dateadd(MONTH,-7,GETDATE())

DELETE LargeTable  WHERE readTime < @DATE
WHILE @@ROWCOUNT > 0
BEGIN
   DELETE LargeTable  WHERE readTime < @DATE
END
SET ROWCOUNT 0

वैकल्पिक;

यदि लेन-देन लॉग सक्षम है, तो लेन-देन लॉग अक्षम करें।

ALTER DATABASE dbname SET RECOVERY SIMPLE;

2

छोटा सिंटेक्स

select 1
WHILE (@@ROWCOUNT > 0)
BEGIN
  DELETE TOP (10000) LargeTable 
  WHERE readTime < dateadd(MONTH,-7,GETDATE())
END

1

यदि आप SQL सर्वर 2016 या उच्चतर का उपयोग कर रहे हैं और यदि आपकी तालिका में स्तंभों के आधार पर विभाजन बनाए गए हैं जिन्हें आप हटाने के लिए प्रयास कर रहे हैं (उदाहरण के लिए टाइमस्टैम्प कॉलम), तो आप विभाजन द्वारा डेटा को हटाने के लिए इस नई कमांड का उपयोग कर सकते हैं।

के साथ TRUNCATE टेबल (विभाजन ({|}}, ... n]))

यह केवल चुने हुए विभाजन में डेटा को हटाएगा और तालिका के भाग से डेटा को हटाने के लिए सबसे कुशल तरीका होना चाहिए क्योंकि यह लेनदेन लॉग नहीं बनाएगा और नियमित ट्रंकट के रूप में तेजी से किया जाएगा, लेकिन सभी डेटा हटाए बिना मेज से।

दोष यह है कि यदि आपकी तालिका विभाजन के साथ सेटअप नहीं है, तो आपको पुराने स्कूल जाने और नियमित दृष्टिकोण के साथ डेटा को हटाने की आवश्यकता है और फिर विभाजन के साथ तालिका को फिर से बनाएं ताकि आप भविष्य में ऐसा कर सकें, जो मैंने किया है। मैंने सम्मिलन प्रक्रिया में विभाजन निर्माण और विलोपन को जोड़ा। मेरे पास 500 मिलियन पंक्तियों के साथ तालिका थी इसलिए यह विलोपन समय को कम करने का एकमात्र विकल्प था।

अधिक जानकारी के लिए नीचे दिए गए लिंक देखें: https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server7

विभाजन के साथ SQL सर्वर 2016 ट्रंकट टेबल

नीचे मैंने पहले डेटा को हटाने के लिए क्या किया था इससे पहले कि मैं इसमें आवश्यक डेटा के साथ विभाजन के साथ तालिका को पुनः बना सकता हूं। यह क्वेरी डेटा डिलीट होने तक निर्दिष्ट समय विंडो के दौरान दिनों तक चलेगी।

:connect <<ServerName>>
use <<DatabaseName>>

SET NOCOUNT ON;
DECLARE @Deleted_Rows INT;
DECLARE @loopnum INT;
DECLARE @msg varchar(100);
DECLARE @FlagDate datetime;
SET @FlagDate =  getdate() - 31;
SET @Deleted_Rows = 1;
SET @loopnum = 1;

/*while (getdate() < convert(datetime,'2018-11-08 14:00:00.000',120))
BEGIN
    RAISERROR( 'WAIT for START' ,0,1) WITH NOWAIT   
    WAITFOR DELAY '00:10:00'
END*/
RAISERROR( 'STARTING PURGE' ,0,1) WITH NOWAIT   

WHILE (1=1)
BEGIN
    WHILE (@Deleted_Rows > 0 AND (datepart(hh, getdate() ) >= 12 AND datepart(hh, getdate() ) <= 20)) -- (getdate() < convert(datetime,'2018-11-08 19:00:00.000',120) )
      BEGIN
       -- Delete some small number of rows at a time
         DELETE TOP (500000)  dbo.<<table_name>>
         WHERE timestamp_column < convert(datetime, @FlagDate,102)
         SET @Deleted_Rows = @@ROWCOUNT;
         WAITFOR DELAY '00:00:01'
         select @msg = 'ROWCOUNT' + convert(varchar,@Deleted_Rows);
         set @loopnum = @loopnum + 1
         if @loopnum > 1000
             begin 
                 begin try
                        DBCC SHRINKFILE (N'<<databasename>>_log' , 0, TRUNCATEONLY)
                        RAISERROR( @msg ,0,1) WITH NOWAIT
                 end try
                 begin catch
                     RAISERROR( 'DBCC SHRINK' ,0,1) WITH NOWAIT  
                 end catch
                 set @loopnum = 1
             end
        END
WAITFOR DELAY '00:10:00'
END 
select getdate()

0

यदि मैं बिना लूप के कहता हूं, तो मैं GOTOsql सर्वर का उपयोग करके बड़ी मात्रा में रिकॉर्ड हटाने के लिए स्टेटमेंट का उपयोग कर सकता हूं । exa।

 IsRepeat:
    DELETE TOP (10000)
    FROM <TableName>
    IF @@ROWCOUNT > 0
         GOTO IsRepeat

इस तरह आप छोटे आकार के डिलीट के साथ बड़ी मात्रा में डेटा हटा सकते हैं।

अगर मुझे और जानकारी चाहिए तो मुझे बताएं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.