লগ ছাড়া এসকিউএল-এ টেবিলের বৃহত ডেটা কীভাবে মুছবেন?


127

আমার কাছে একটি বড় ডেটা টেবিল রয়েছে। এই সারণীতে 10 মিলিয়ন রেকর্ড রয়েছে।

এই ক্যোয়ারির জন্য সর্বোত্তম উপায় কী

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

4
:) আমি ভয় করি যতক্ষণ না আপনি সমস্ত সারি পড়ার জন্য কিছু প্রকারের ETL লিখতে ইচ্ছুক হন তবে টাইম> = তারিখ যুক্ত (মাস, -7, GETDATE ()) অন্য সারণীতে এবং তারপরে একটি কাটা টেবিল ইস্যু করে এবং ETL ব্যবহার করে ডেটা ফিরিয়ে না দেওয়া , আপনি এটিকে
লগতে

লগইন একটি স্থিতিশীল লেনদেন থাকার সমস্ত বা কিছুই ফাংশন। আক্ষরিক অর্থে কিছু অপারেশনের জন্য লগ না থাকলেও অন্যদের নয়, অন্যথায় লগটি অকেজো।
এরিক ফিলিপস

1
আপনি যে ডেটা রাখতে চান তা রফতানি করুন, টেবিলটি কেটে ফেলুন, তারপরে আবার আমদানি করুন
বোহেমিয়ান

অন্য বিকল্পটি একটি টেবিল ভারিয়েবল ব্যবহার করবে যা লগ নেই। সুতরাং আপনার রিডটাইম> = তারিখসংযুক্ত (MONTH, -7, GETDATE ()) ডেটা একটি সারণী ভেরিয়েবলে সঞ্চয় করুন এবং তারপরে মূল টেবিলটি কেটে ফেলুন এবং সারণী ভেরিয়েবল থেকে ডেটা অনুলিপি করুন। যাইহোক কিছু ভুল হয়ে যায় এবং টেবিলটি অজান্তে ছাঁটাই হয়ে যায় সে ক্ষেত্রে আমি ডেটা ব্যাক আপ রাখব :) :) এবং সর্বদা কম পরিবেশে আপনার স্ক্রিপ্টটির একটি পরীক্ষা চালান।
TMNT2014

উত্তর:


203
  1. যদি আপনি সেই টেবিলের সমস্ত সারি মুছে ফেলছেন তবে সর্বাধিক সহজ বিকল্পটি হ'ল টেবিল কেটে দেওয়া, এরকম কিছু something

    TRUNCATE TABLE LargeTable
    GO

    কাটা টেবিলটি কেবল টেবিলটি খালি করে দেবে, আপনি সারিগুলি মুছে ফেলা সীমাবদ্ধ করার জন্য WHERE ধারাটি ব্যবহার করতে পারবেন না এবং কোনও ট্রিগারকে বরখাস্ত করা হবে না।

  2. অন্যদিকে আপনি যদি ৮০-৯০ শতাংশেরও বেশি ডেটা মুছে ফেলছেন তবে বলুন যে আপনার কাছে মোট ১১ মিলিয়ন সারি রয়েছে এবং আপনি ১০ মিলিয়ন মুছে ফেলতে চান অন্যভাবে এই 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
এছাড়াও মনে রাখবেন যে আপনি যদি কোনও টেবিল কেটে ফেলেন তবে আপনার সাথে কোনও এফকে এসোসিয়েটেড করা যাবে না।
এইচএলজিইএম

1
তবে আপনি কীভাবে 80-90% ডেটা মুছে ফেলছেন তা নিশ্চিত হওয়া যায়? আসুন ধরে নেওয়া যাক আমার কাছে কেবলমাত্র মানের সীমা আছে যা মুছতে হবে। এবং আমি কয়েকটি টেবিল আছে। সুতরাং আমি তাদের প্রত্যেকটি পরীক্ষা করে দেখতে এবং শতাংশ নির্ধারণ করতে হবে, এবং যদি এটি প্রায় 30% আমার ধারণা হয় যে এই পদ্ধতিটি খুব কার্যকর নয় ... আমি অজানা মামলার অনুকূল সমাধান অনুসন্ধান করার চেষ্টা করছি।
আর্চন্ট

7
@ আরকন্ট optimal solution for unknown caseএটাই কি স্বপ্ন না? দুর্ভাগ্যক্রমে আপনি কোনও একটি বড়ি দিয়ে প্রতিটি রোগ নিরাময় করতে পারবেন না; আমি বিভিন্ন পরিস্থিতিতে কিছু সম্ভাব্য সমাধানের পরামর্শ দিয়েছি। দুর্ভাগ্যক্রমে এখানে কোনও স্লিভার বুলেট নেই।
এম.আলি

5
বিকল্প 4 বেছে নেওয়ার ক্ষেত্রে একটি বিষয় লক্ষণীয়: টেবিলটি কীভাবে ব্যবহৃত হবে তার উপর নির্ভর করে লক বৃদ্ধি এড়াতে একবারে 5000 টিরও কম সারি মুছে ফেলা ভাল বিকল্প হতে পারে ।
ড্যানিয়েল

যদি মুছে ফেলতে রেকর্ডগুলির গণনাটি আরও বেশি থাকে তবে রেকর্ডগুলি যা টেবিলে থাকবে, আমি দেখতে পেয়েছি যে রেকর্ডগুলির টেম্প টেবিলের মধ্যে সরল নির্বাচনটি থাকবে যা মূল টেবিলের মধ্যে থাকবে এবং টেম্প টেবিলটির নাম পরিবর্তন করা খুব দ্রুত। আপনি কোথাও পরিচয় আইডি বিদেশী কী ব্যবহার করবেন না তা দেওয়া।
ভ্লাদিমির বোজিক

95

@ এম-আলি উত্তরটি সঠিক তবে এটিও মনে রাখবেন যে আপনি প্রতিটি অংশের পরে লেনদেন না করলে এবং একটি চেকপয়েন্ট সম্পাদন না করলে লগগুলি প্রচুর পরিমাণে বাড়তে পারে। পারফরম্যান্স টেস্ট এবং গ্রাফ সহ আমি এটি এইভাবে করব এবং এই নিবন্ধটি http://sqlperformance.com/2013/03/io-subs systemm/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)উইন্ড-লুপটি আবার কার্যকর করবে এমনকি যদি এটি কেবল একটি সারি মুছে ফেলে, এবং পরবর্তী সম্পাদনাটি মুছে ফেলার জন্য একটি সারি বা দুটিও খুঁজে পেতে পারে - যার ফলে অসীম লুপ তৈরি হয়।
এনএস ডু টোইট

@ এনএসইউ টুয়েট WHERE শর্তটি কমপক্ষে months মাস পুরানো রেকর্ড বিবেচনা করছে যাতে আপনি মুছে ফেলার সময় এমন শর্ত পূরণ করে এমন নতুন রেকর্ড থাকবে না।
ফ্রান্সিসকো গোল্ডেনস্টাইন

@ ফ্রেঞ্চিসকো গোল্ডেনস্টেইন ওয়েল, আপনি বার বার WHILEলুপের মধ্যে তারিখ গণনা করার সাথে সাথে ক্যোয়ারীতে ব্যবহৃত তারিখটি প্রতিটি পুনরাবৃত্তির সাথে আলাদা হবে :dateadd(MONTH,-7,GETDATE())
এনএস ডু টোইট

@ ফ্রেঞ্চিসকো গোল্ডেনস্টাইন এছাড়াও, সম্ভবত এটির চেয়ে অন্য ব্যবহারের ক্ষেত্রে - সম্ভবত অন্তর্নিহিত টেবিলটিতে নতুন ডেটা যুক্ত হবে যার ফলে নতুন রেকর্ড তৈরি হবে যা WHILEলুপের বিভিন্ন পুনরাবৃত্তির মধ্যে মুছে ফেলা হতে পারে ।
এনএস ডু টোইট

52

আপনি একই প্রশ্নটি কতবার কার্যকর করতে চান তা আপনি জিও + ব্যবহার করতে পারেন।

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

আমি এটি পছন্দ করি, এটি আমার জন্য কাজ করছে আমি ঘটনাক্রমে একই সারিটি 26 মিলিয়ন বার একটি টেবিলের মধ্যে sertedুকিয়ে দিয়েছিলাম এবং এর সমস্ত উপস্থিতি মুছে ফেলার প্রয়োজন হয়েছিল, যা একক মুছে ফেলার বিবৃতিতে সার্ভারের স্মৃতি থেকে বেরিয়ে যায়, তাই এটি দুর্দান্ত প্রশ্ন , এটি মুছে ফেলা সারি শেষ হয়ে গেলে মিড লুপটি থামবে?
স্কটসি

2
@ স্কটসি, এটি কোনও লুপ নয়, এটি কেবল ক্যোরির পুনরাবৃত্তি করে (ব্যাচের মতো) এবং যদি আপনি সারিগুলি সরিয়ে চলে যান তবে এটি কিছুই মুছতে পারে না। তবে তা থামবে না। আপনি মুছে ফেলা সারিটি শেষ হয়ে গেলে আপনি (0 টি সারি (গুলি) এর মতো কিছু পাবেন)।
বুঙ্কারবাস্টার

আহ, হ্যাঁ আমি আবিষ্কার করেছি যে আমার প্রশ্ন পোস্ট করার প্রায় 5 মিনিট পরে, যেহেতু আমার মোছা শেষ হয়েছে, ধন্যবাদ এটি খুব সহায়ক ছিল!
স্কটসি

1
কোন এমএস এসকিউএল সার্ভার থেকে এই সিনট্যাক্সটি GO xxকাজ করার কথা? আমি একটি "সঞ্চিত পদ্ধতি খুঁজে পাইনি" " ত্রুটি পেয়েছি । GOকমান্ড ব্যতীত এটি ঠিক কাজ করে।
আবেল

3
হুম, মনে হচ্ছে আমি এটি কার্যকর করতে পারি, এবং এটি প্রকৃতপক্ষে একাধিকবার চলে, তবে এমএস এসকিউএল এমজিটি স্টুডিওতে এটি উল্লিখিত ত্রুটির সাথে লাল কোঁকড়ানো রেখাটি দেখায় (তবে F5- রান কাজ করে)
অ্যাবেল

11

@ ফ্রেঞ্চিসকো গোল্ডেনস্টাইন, কেবলমাত্র একটি ছোট্ট সংশোধন। আপনার ভেরিয়েবল সেট করার পরে কমিট অবশ্যই ব্যবহার করা উচিত, অন্যথায় 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

এম.আলির এই প্রকরণটি আমার পক্ষে ভাল কাজ করছে। এটি কিছু মুছে দেয়, লগ সাফ করে এবং পুনরাবৃত্তি করে। আমি লগটি বাড়তে দেখছি, ড্রপ করে আবার শুরু করব।

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. বড় সারণীর প্রাথমিক কীটির ন্যূনতম মান পান যেখানে রিডটাইম <ডেটডিড (মাস, -7, GETDATE ())। (রিডটাইমে সূচি যোগ করুন, ইতিমধ্যে উপস্থিত না থাকলে, এই সূচিটি 3 ধাপে টেবিলের সাথে যাইহোক মুছে ফেলা হবে)। এটি একটি চলক 'min_primary' এ সঞ্চয় করতে দেয়

  2. প্রাথমিক কী> মিনি_প্রিমারিযুক্ত সমস্ত সারি একটি মঞ্চ টেবিলটিতে সন্নিবেশ করান (সারিগুলির সংখ্যা যদি বড় না হয় তবে মেমরি টেবিল)।

  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

আপনি যদি এসকিউএল সার্ভার 2016 বা তার বেশি ব্যবহার করছেন এবং যদি আপনার টেবিলটিতে কলামের ভিত্তিতে পার্টিশন তৈরি করা হচ্ছে আপনি মুছে ফেলার চেষ্টা করছেন (উদাহরণস্বরূপ টাইমস্ট্যাম্প কলাম), তবে আপনি পার্টিশন দ্বারা ডেটা মুছতে এই নতুন কমান্ডটি ব্যবহার করতে পারেন।

টেবিলের সাথে কাটা (পার্টিশনগুলি ({| ... [, ... n]))

এটি কেবলমাত্র নির্বাচিত পার্টিশনগুলিতে ডেটা মুছে ফেলবে এবং টেবিলের অংশ থেকে ডেটা মুছে ফেলার সবচেয়ে কার্যকর উপায় হওয়া উচিত কারণ এটি লেনদেনের লগগুলি তৈরি করে না এবং এটি নিয়মিত কাটা কাটা হিসাবে দ্রুত করা হবে তবে সমস্ত ডেটা মোছা না করেই করা হবে টেবিল থেকে।

ত্রুটি হ'ল যদি আপনার টেবিলটি পার্টিশনের সাথে সেটআপ না করা হয়, তবে আপনাকে পুরানো স্কুলে যেতে হবে এবং নিয়মিত পদ্ধতির সাহায্যে ডেটা মুছতে হবে এবং তারপরে পার্টিশন সহ টেবিলটি পুনরায় তৈরি করতে হবে যাতে ভবিষ্যতে আপনি এটি করতে পারেন যা আমি এটি করেছি। আমি সন্নিবেশ পদ্ধতিতে বিভাজন তৈরি এবং মুছে ফেলা যুক্ত করেছি। আমার 500 মিলিয়ন সারি সহ টেবিল ছিল তাই মোছার সময় হ্রাস করার একমাত্র বিকল্প এটি ছিল।

আরও তথ্যের জন্য নীচের লিঙ্কগুলি দেখুন: https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017

এসকিউএল সার্ভার 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

যদি আমি লুপ ছাড়াই বলি, আমি GOTOস্কেল সার্ভার ব্যবহার করে প্রচুর পরিমাণে রেকর্ড মুছতে স্টেটমেন্টটি ব্যবহার করতে পারি । exa।

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

আপনি মুছে ফেলতে পারেন এর আকারের মতো আপনি বড় পরিমাণে ডেটা মুছতে পারেন।

আরও তথ্যের প্রয়োজন হলে আমাকে জানান।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.