मुझे इस प्रश्न के बारे में पता नहीं था जब मैंने संबंधित प्रश्न का उत्तर दिया ( लूप में इसमें स्पष्ट लेनदेन आवश्यक हैं? ), लेकिन पूर्णता के लिए, मैं इस मुद्दे को यहां संबोधित करूंगा क्योंकि यह उस लिंक किए गए उत्तर में मेरे सुझाव का हिस्सा नहीं था ।
चूँकि मैं SQL एजेंट जॉब के माध्यम से इसे शेड्यूल करने का सुझाव दे रहा हूँ (यह 100 मिलियन पंक्तियाँ हैं, आखिरकार), मुझे नहीं लगता कि क्लाइंट (यानी SSMS) को स्टेटस मैसेज भेजने का कोई भी रूप आदर्श होगा (हालांकि अगर ऐसा है तो) कभी अन्य परियोजनाओं की आवश्यकता होती है, तो मैं व्लादिमीर के साथ सहमत हूं कि उपयोग करने RAISERROR('', 10, 1) WITH NOWAIT;
का तरीका है)।
इस विशेष मामले में, मैं एक स्टेटस टेबल बनाऊंगा जिसे प्रत्येक लूप के अनुसार अपडेट किया जा सकता है और अब तक अपडेट की गई पंक्तियों की संख्या। और यह मौजूदा समय में फेंकने के लिए प्रक्रिया पर दिल की धड़कन को चोट नहीं पहुंचाता है।
यह देखते हुए कि आप प्रक्रिया को रद्द करना और पुनः आरंभ करना चाहते हैं, मैं एक स्पष्ट लेनदेन में स्थिति तालिका के अद्यतन के साथ मुख्य तालिका के UPDATE को लपेटने से थका हुआ हूं। हालाँकि, यदि आपको लगता है कि रद्द करने के कारण स्थिति तालिका कभी भी सिंक से बाहर है, तो बस इसे मैन्युअल रूप से अपडेट करके वर्तमान मूल्य के साथ ताज़ा करना आसान है COUNT(*) FROM [huge-table] WHERE deleted IS NOT NULL AND deletedDate IS NOT NULL
।और अद्यतन करने के लिए दो टेबल हैं (यानी मुख्य तालिका और स्थिति तालिका), हमें उन दो तालिकाओं को सिंक में रखने के लिए एक स्पष्ट लेनदेन का उपयोग करना चाहिए , फिर भी यदि आप प्रक्रिया को रद्द करते हैं तो हम अनाथ लेनदेन का जोखिम नहीं उठाना चाहते हैं बिंदु के बाद यह लेनदेन शुरू कर दिया है, लेकिन यह प्रतिबद्ध नहीं है। यह तब तक करने के लिए सुरक्षित होना चाहिए जब तक आप SQL एजेंट काम को बंद नहीं करते।
आप um के बिना प्रक्रिया को कैसे रोक सकते हैं, ठीक है, इसे रोक सकते हैं? इसे रोकने के लिए :-) हां। प्रक्रिया को एक "सिग्नल" ( kill -3
यूनिक्स में समान ) भेजकर , आप अनुरोध कर सकते हैं कि यह अगले सुविधाजनक क्षण में बंद हो जाए (यानी जब कोई सक्रिय लेनदेन न हो!) और इसे सभी अच्छे और सुव्यवस्थित तरह से साफ करें।
आप दूसरे सत्र में चल रही प्रक्रिया के साथ कैसे संवाद कर सकते हैं? उसी तंत्र का उपयोग करके जो हमने इसके लिए बनाया है ताकि आप इसकी वर्तमान स्थिति को वापस आप तक पहुंचा सकें: स्थिति तालिका। हमें बस एक कॉलम जोड़ने की आवश्यकता है जो प्रक्रिया प्रत्येक लूप की शुरुआत में जांच करेगी ताकि यह पता चले कि आगे बढ़ना है या गर्भपात करना है। और चूंकि आशय इसे SQL एजेंट की नौकरी के रूप में शेड्यूल करना है (हर 10 या 20 मिनट पर रन करें), हमें शुरुआत में ही जांच कर लेनी चाहिए क्योंकि अगर प्रक्रिया अभी चल रही है तो 1 मिलियन पंक्तियों के साथ एक अस्थायी तालिका भरने का कोई मतलब नहीं है। एक पल बाद बाहर निकलने के लिए और उस डेटा का कोई उपयोग नहीं करने के लिए।
DECLARE @BatchRows INT = 1000000,
@UpdateRows INT = 4995;
IF (OBJECT_ID(N'dbo.HugeTable_TempStatus') IS NULL)
BEGIN
CREATE TABLE dbo.HugeTable_TempStatus
(
RowsUpdated INT NOT NULL, -- updated by the process
LastUpdatedOn DATETIME NOT NULL, -- updated by the process
PauseProcess BIT NOT NULL -- read by the process
);
INSERT INTO dbo.HugeTable_TempStatus (RowsUpdated, LastUpdatedOn, PauseProcess)
VALUES (0, GETDATE(), 0);
END;
-- First check to see if we should run. If no, don't waste time filling temp table
IF (EXISTS(SELECT * FROM dbo.HugeTable_TempStatus WHERE PauseProcess = 1))
BEGIN
PRINT 'Process is paused. No need to start.';
RETURN;
END;
CREATE TABLE #FullSet (KeyField1 DataType1, KeyField2 DataType2);
CREATE TABLE #CurrentSet (KeyField1 DataType1, KeyField2 DataType2);
INSERT INTO #FullSet (KeyField1, KeyField2)
SELECT TOP (@BatchRows) ht.KeyField1, ht.KeyField2
FROM dbo.HugeTable ht
WHERE ht.deleted IS NULL
OR ht.deletedDate IS NULL
WHILE (1 = 1)
BEGIN
-- Check if process is paused. If yes, just exit cleanly.
IF (EXISTS(SELECT * FROM dbo.HugeTable_TempStatus WHERE PauseProcess = 1))
BEGIN
PRINT 'Process is paused. Exiting.';
BREAK;
END;
-- grab a set of rows to update
DELETE TOP (@UpdateRows)
FROM #FullSet
OUTPUT Deleted.KeyField1, Deleted.KeyField2
INTO #CurrentSet (KeyField1, KeyField2);
IF (@@ROWCOUNT = 0)
BEGIN
RAISERROR(N'All rows have been updated!!', 16, 1);
BREAK;
END;
BEGIN TRY
BEGIN TRAN;
-- do the update of the main table
UPDATE ht
SET ht.deleted = 0,
ht.deletedDate = '2000-01-01'
FROM dbo.HugeTable ht
INNER JOIN #CurrentSet cs
ON cs.KeyField1 = ht.KeyField1
AND cs.KeyField2 = ht.KeyField2;
-- update the current status
UPDATE ts
SET ts.RowsUpdated += @@ROWCOUNT,
ts.LastUpdatedOn = GETDATE()
FROM dbo.HugeTable_TempStatus ts;
COMMIT TRAN;
END TRY
BEGIN CATCH
IF (@@TRANCOUNT > 0)
BEGIN
ROLLBACK TRAN;
END;
THROW; -- raise the error and terminate the process
END CATCH;
-- clear out rows to update for next iteration
TRUNCATE TABLE #CurrentSet;
WAITFOR DELAY '00:00:01'; -- 1 second delay for some breathing room
END;
-- clean up temp tables when testing
-- DROP TABLE #FullSet;
-- DROP TABLE #CurrentSet;
आप निम्न क्वेरी का उपयोग करके किसी भी समय स्थिति की जांच कर सकते हैं:
SELECT sp.[rows] AS [TotalRowsInTable],
ts.RowsUpdated,
(sp.[rows] - ts.RowsUpdated) AS [RowsRemaining],
ts.LastUpdatedOn
FROM sys.partitions sp
CROSS JOIN dbo.HugeTable_TempStatus ts
WHERE sp.[object_id] = OBJECT_ID(N'ResizeTest')
AND sp.[index_id] < 2;
प्रक्रिया को विराम देना चाहते हैं, चाहे वह SQL एजेंट नौकरी में चल रहा हो या SSMS में किसी और के कंप्यूटर पर भी? बस दौडो:
UPDATE ht
SET ht.PauseProcess = 1
FROM dbo.HugeTable_TempStatus ts;
क्या प्रक्रिया फिर से शुरू करने में सक्षम होना चाहते हैं? बस दौडो:
UPDATE ht
SET ht.PauseProcess = 0
FROM dbo.HugeTable_TempStatus ts;
अपडेट करें:
यहाँ कुछ अतिरिक्त चीजें हैं जो इस ऑपरेशन के प्रदर्शन में सुधार कर सकती हैं। किसी को भी मदद करने की गारंटी नहीं है, लेकिन शायद बाहर परीक्षण के लायक हैं। और अपडेट करने के लिए 100 मिलियन पंक्तियों के साथ, आपके पास कुछ भिन्नताओं का परीक्षण करने के लिए बहुत समय / अवसर है;;
TOP (@UpdateRows)
UPDATE क्वेरी में जोड़ें , ताकि शीर्ष पंक्ति दिखे:
UPDATE TOP (@UpdateRows) ht
कभी-कभी यह ऑप्टिमाइज़र को यह जानने में मदद करता है कि अधिकतम कितनी पंक्तियाँ प्रभावित होंगी ताकि यह अधिक समय की तलाश में बर्बाद न हो।
#CurrentSet
अस्थायी तालिका में एक प्राथमिक कुंजी जोड़ें । यहाँ विचार 100 मिलियन रो टेबल के लिए JOIN के साथ ऑप्टिमाइज़र की मदद करना है।
और बस यह कहा गया है ताकि अस्पष्ट न हो, #FullSet
अस्थायी तालिका में पीके को जोड़ने का कोई कारण नहीं होना चाहिए क्योंकि यह सिर्फ एक सरल कतार तालिका है जहां आदेश अप्रासंगिक है।
- कुछ मामलों में यह टेम्परिंग इंडेक्स को जोड़ने में मदद करता है ताकि टेम्प टेबल
SELECT
में फीड हो सके #FullSet
। इस तरह के सूचकांक को जोड़ने से संबंधित कुछ विचार इस प्रकार हैं:
- WHERE की स्थिति को आपकी क्वेरी की WHERE की स्थिति से मेल खाना चाहिए, इसलिए
WHERE deleted is null or deletedDate is null
- प्रक्रिया की शुरुआत में, अधिकांश पंक्तियाँ आपके WHERE की स्थिति से मेल खाएँगी, इसलिए कोई अनुक्रमणिका सहायक नहीं है। इसे जोड़ने से पहले आप 50% के आसपास कहीं इंतजार करना चाहते हैं। बेशक, यह कितना मदद करता है और जब सूचकांक को जोड़ना सबसे अच्छा होता है तो कई कारकों के कारण अलग-अलग होते हैं, इसलिए यह थोड़ा परीक्षण और त्रुटि है।
- आपको बेस डेटा को बार-बार बदल रहा है क्योंकि आपको इसे अपडेट रखने के लिए मैन्युअल रूप से अपडेट और / या रिब्यूल्ड करना पड़ सकता है।
- यह ध्यान रखें कि मदद करते समय सूचकांक
SELECT
को चोट लगेगी, UPDATE
क्योंकि यह एक अन्य ऑब्जेक्ट है जो उस ऑपरेशन के दौरान अपडेट होना चाहिए, इसलिए अधिक I / O। यह फ़िल्टर किए गए इंडेक्स का उपयोग करके दोनों में खेलता है (जो कि कम पंक्तियों को फ़िल्टर से मेल खाने के बाद से आप अपडेट करते हैं), और इंडेक्स को जोड़ने के लिए थोड़ी प्रतीक्षा कर रहे हैं (यदि यह शुरुआत में सुपर सहायक नहीं हो रहा है, तो कोई कारण नहीं है अतिरिक्त I / O)।