अगर मेरे पास एक अद्यतन विवरण है जो वास्तव में कोई डेटा नहीं बदलता है (क्योंकि डेटा पहले से ही अद्यतन स्थिति में है), तो क्या चेक में कोई रोक लगाने में कोई प्रदर्शन लाभ है जहां अपडेट को रोकने के लिए है?
निश्चित रूप से वहाँ हो सकता है क्योंकि UPDATE 1 के कारण थोड़ा सा प्रदर्शन अंतर है :
- वास्तव में किसी भी पंक्तियों को अपडेट नहीं करना (इसलिए डिस्क पर लिखने के लिए कुछ भी नहीं, न्यूनतम लॉग गतिविधि भी नहीं), और
- वास्तविक अद्यतन करने के लिए जो आवश्यक हो उससे कम प्रतिबंधात्मक ताले को निकालना (इसलिए संगामिति के लिए बेहतर है) ( कृपया अंत तक अद्यतन अनुभाग देखें )
हालाँकि, आपके स्कीमा, और डेटा, और सिस्टम लोड के साथ आपके सिस्टम पर आपके द्वारा मापा जाने वाले अंतर में से कितना अंतर होगा। अपडेट न होने वाले UPDATE में कितने कारक हैं, जो कितना प्रभाव डालते हैं:
- अद्यतन की जा रही तालिका पर विवाद की मात्रा
- अपडेट की जा रही पंक्तियों की संख्या
- यदि अद्यतन किए जा रहे टेबल पर अद्यतन ट्रिगर हैं (जैसा कि प्रश्न पर टिप्पणी में मार्क द्वारा नोट किया गया है)। यदि आप निष्पादित करते हैं
UPDATE TableName SET Field1 = Field1
, तो एक अपडेट ट्रिगर फायर करेगा और इंगित करेगा कि फ़ील्ड अपडेट की गई थी (यदि आप या तो अद्यतन करें) या ( COLUMNS_UPDATED फ़ंक्शंस का उपयोग करके ), और यह कि दोनों INSERTED
और DELETED
तालिकाओं में फ़ील्ड एक ही मान हैं।
इसके अलावा, पॉल व्हाइट के लेख, द इम्पैक्ट ऑफ़ नॉन-अपडेटिंग अपडेट्स में निम्नलिखित सारांश अनुभाग पाया गया है (जैसा कि उनके जवाब में एक टिप्पणी में @spaghettidba द्वारा उल्लेख किया गया है):
SQL सर्वर में अनावश्यक लॉगिंग या पृष्ठ फ्लशिंग से बचने के लिए कई अनुकूलन होते हैं, जब एक UPDATE ऑपरेशन को संसाधित करते हैं जो लगातार डेटाबेस में किसी भी परिवर्तन का परिणाम नहीं होगा।
- क्लस्टर तालिका में गैर-अपडेट करने वाले अपडेट आम तौर पर अतिरिक्त लॉगिंग और पेज फ्लशिंग से बचते हैं, जब तक कि स्तंभ का एक भाग (कुंजी का हिस्सा) अपडेट ऑपरेशन से प्रभावित नहीं होता है।
- यदि क्लस्टर कुंजी का कोई भी भाग समान मान के लिए 'अपडेट' है, तो ऑपरेशन लॉग किया जाता है जैसे कि डेटा बदल गया था, और प्रभावित पृष्ठ बफर पूल में गंदे के रूप में चिह्नित हैं। यह डिलीट-तब-डालने के ऑपरेशन के लिए UPDATE के रूपांतरण का परिणाम है।
- हीप टेबल किसी भी अतिरिक्त लॉगिंग या पेज फ्लशिंग का कारण बनने के लिए क्लस्टर कुंजी नहीं है, सिवाय टेबल टेबल के समान व्यवहार करते हैं। यह तब भी बना रहता है, जहां ढेर पर गैर-संकुल प्राथमिक कुंजी मौजूद होती है। एक ढेर के लिए गैर-अपडेटिंग अपडेट इसलिए आमतौर पर अतिरिक्त लॉगिंग और फ्लशिंग (लेकिन नीचे देखें) से बचें।
- दोनों ढेर और गुच्छेदार तालिकाओं को किसी भी पंक्ति के लिए अतिरिक्त लॉगिंग और फ्लशिंग का सामना करना पड़ेगा जहां एक LOB स्तंभ जिसमें 8000 बाइट्स से अधिक डेटा होता है, उन्हें 'SET column_name = column_name' के अलावा किसी भी सिंटैक्स का उपयोग करके उसी मान में अपडेट किया जाता है।
- बस डेटाबेस पर किसी भी प्रकार की पंक्ति संस्करण आइसोलेशन स्तर को सक्षम करना हमेशा अतिरिक्त लॉगिंग और फ्लशिंग का कारण बनता है। यह अद्यतन लेन-देन के लिए प्रभाव में अलगाव स्तर की परवाह किए बिना होता है।
कृपया ध्यान रखें (विशेषकर यदि आप पॉल के पूर्ण लेख को देखने के लिए लिंक का अनुसरण नहीं करते हैं), निम्नलिखित दो आइटम:
गैर-अद्यतन अपडेट में अभी भी कुछ लॉग गतिविधि है, जिसमें दिखाया गया है कि लेनदेन शुरू हो रहा है और समाप्त हो रहा है। यह सिर्फ इतना है कि कोई डेटा संशोधन नहीं होता है (जो अभी भी एक अच्छी बचत है)।
जैसा कि मैंने ऊपर कहा है, आपको अपने सिस्टम पर परीक्षण करने की आवश्यकता है। पॉल जो उपयोग कर रहा है उन्हीं शोध प्रश्नों का उपयोग करें और देखें कि क्या आपको वही परिणाम मिलते हैं। मैं अपने सिस्टम पर थोड़ा अलग परिणाम देख रहा हूं जो लेख में दिखाया गया है। अभी भी कोई गंदा पृष्ठ नहीं लिखा जाना है, लेकिन थोड़ी और लॉग गतिविधि।
... मुझे अपरिवर्तित पंक्ति को शामिल करने के लिए पंक्ति गणना की आवश्यकता है, इसलिए मुझे पता है कि क्या आईडी सम्मिलित नहीं है या नहीं। ... क्या यह संभव है कि मुझे किसी तरह की पंक्ति की गिनती मिल जाए?
सरलीकृत रूप से, यदि आप केवल एक पंक्ति के साथ काम कर रहे हैं, तो आप निम्न कार्य कर सकते हैं:
UPDATE MyTable
SET Value = 2
WHERE ID = 2
AND Value <> 2;
IF (@@ROWCOUNT = 0)
BEGIN
IF (NOT EXISTS(
SELECT *
FROM MyTable
WHERE ID = 2 -- or Value = 2 depending on the scenario
)
)
BEGIN
INSERT INTO MyTable (ID, Value) -- or leave out ID if it is an IDENTITY
VALUES (2, 2);
END;
END;
कई पंक्तियों के लिए, आप का उपयोग करके निर्णय लेने के लिए आवश्यक जानकारी प्राप्त कर सकते हैं OUTPUT
क्लॉज । वास्तव में क्या पंक्तियों को अद्यतन करके कैप्चर किया गया था, फिर आप पंक्तियों को अपडेट न करने के बीच अंतर जानने के लिए आइटमों को संकीर्ण कर सकते हैं जो मौजूद पंक्तियों को अपडेट नहीं करने के विरोध में मौजूद नहीं हैं लेकिन अपडेट की आवश्यकता नहीं है।
मैं निम्नलिखित उत्तर में मूल कार्यान्वयन दिखाता हूं:
एक्सएमएल पैरामीटर का उपयोग करके कई डेटा को अपग्रेड करते समय मर्ज क्वेरी का उपयोग करने से कैसे बचें?
उस उत्तर में दिखाई गई विधि उन पंक्तियों को फ़िल्टर नहीं करती है जो अभी तक मौजूद हैं उन्हें अपडेट करने की आवश्यकता नहीं है। उस हिस्से को जोड़ा जा सकता है, लेकिन आपको सबसे पहले यह दिखाने की जरूरत होगी कि आप अपने डेटासेट को कहां से विलय कर रहे हैं MyTable
। क्या वे एक अस्थायी तालिका से आ रहे हैं? एक टेबल-वैल्यू पैरामीटर (TVP)?
अद्यतन 1:
मैं अंत में कुछ परीक्षण करने में सक्षम था और लेन-देन लॉग और लॉकिंग के बारे में मुझे यही मिला। सबसे पहले, तालिका के लिए स्कीमा:
CREATE TABLE [dbo].[Test]
(
[ID] [int] NOT NULL CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED,
[StringField] [varchar](500) NULL
);
इसके बाद, परीक्षण क्षेत्र को उस मूल्य पर अद्यतन करता है जो पहले से है:
UPDATE rt
SET rt.StringField = '04CF508B-B78E-4264-B9EE-E87DC4AD237A'
FROM dbo.Test rt
WHERE rt.ID = 4082117
परिणाम:
-- Transaction Log (2 entries):
Operation
----------------------------
LOP_BEGIN_XACT
LOP_COMMIT_XACT
-- SQL Profiler (3 Lock:Acquired events):
Mode Type
--------------------------------------
8 - IX 5 - OBJECT
8 - IX 6 - PAGE
5 - X 7 - KEY
अंत में, परीक्षण जो मान को नहीं बदलने के कारण अद्यतन को फ़िल्टर करता है:
UPDATE rt
SET rt.StringField = '04CF508B-B78E-4264-B9EE-E87DC4AD237A'
FROM dbo.Test rt
WHERE rt.ID = 4082117
AND rt.StringField <> '04CF508B-B78E-4264-B9EE-E87DC4AD237A';
परिणाम:
-- Transaction Log (0 entries):
Operation
----------------------------
-- SQL Profiler (3 Lock:Acquired events):
Mode Type
--------------------------------------
8 - IX 5 - OBJECT
7 - IU 6 - PAGE
4 - U 7 - KEY
जैसा कि आप देख सकते हैं, पंक्ति को फ़िल्टर करते समय ट्रांजेक्शन लॉग के लिए कुछ भी नहीं लिखा गया है, जैसा कि लेन-देन की शुरुआत और समाप्ति को चिह्नित करने वाली दो प्रविष्टियों के विपरीत है। और जबकि यह सच है कि उन दो प्रविष्टियों में लगभग कुछ भी नहीं है, वे अभी भी कुछ हैं।
जब पंक्तियाँ बदली नहीं जाती हैं, तो पृष्ठ और कुंजी संसाधनों के लॉकिंग कम प्रतिबंधक होते हैं। यदि कोई अन्य प्रक्रिया इस तालिका के साथ बातचीत नहीं कर रही है, तो यह संभवतः एक गैर-मुद्दा है (लेकिन इसकी संभावना कितनी है, वास्तव में?)। ध्यान रखें कि किसी भी लिंक किए गए ब्लॉग (और यहां तक कि मेरे परीक्षण) में दिखाए गए परीक्षण का तात्पर्य यह है कि टेबल पर कोई विवाद नहीं है क्योंकि यह परीक्षण का हिस्सा नहीं है। यह कहते हुए कि गैर-अद्यतन अपडेट इतने हल्के-वजन के होते हैं कि यह करने के लिए फ़िल्टर करने की आवश्यकता नहीं होती है क्योंकि नमक को दाने के साथ लेना पड़ता है, क्योंकि परीक्षण शून्य से कम या ज्यादा किया गया है। लेकिन उत्पादन में, यह तालिका सबसे अलग-थलग होने की संभावना नहीं है। बेशक, यह बहुत अच्छी तरह से हो सकता है कि थोड़ा सा लॉगिंग और अधिक प्रतिबंधात्मक ताले कम दक्षता में अनुवाद नहीं करते हैं। तो इस सवाल का जवाब देने के लिए जानकारी का सबसे विश्वसनीय स्रोत? एस क्यू एल सर्वर। विशेष रूप से:आपका SQL सर्वर। यह आपको दिखाएगा कि आपके सिस्टम के लिए कौन सा तरीका बेहतर है :-)।
अद्यतन 2:
यदि संचालन जिसमें नया मूल्य वर्तमान मूल्य (यानी कोई अद्यतन नहीं) के समान है, तो ऐसे संचालन की संख्या जिसमें नया मूल्य अलग है और अद्यतन आवश्यक है, तो निम्न पैटर्न और भी बेहतर साबित हो सकता है, खासकर यदि मेज पर बहुत विवाद है। SELECT
वर्तमान मूल्य प्राप्त करने के लिए एक सरल पहले करने के लिए विचार है । यदि आपको कोई मूल्य नहीं मिलता है, तो आपके पास अपना जवाब है INSERT
। यदि आपके पास एक मूल्य है, तो आप एक सरल कर सकते हैं IF
और UPDATE
केवल तभी जारी कर सकते हैं जब इसकी आवश्यकता हो।
DECLARE @CurrentValue VARCHAR(500) = NULL,
@NewValue VARCHAR(500) = '04CF508B-B78E-4264-B9EE-E87DC4AD237A',
@ID INT = 4082117;
SELECT @CurrentValue = rt.StringField
FROM dbo.Test rt
WHERE rt.ID = @ID;
IF (@CurrentValue IS NULL) -- if NULL is valid, use @@ROWCOUNT = 0
BEGIN
-- row does not exist
INSERT INTO dbo.Test (ID, StringField)
VALUES (@ID, @NewValue);
END;
ELSE
BEGIN
-- row exists, so check value to see if it is different
IF (@CurrentValue <> @NewValue)
BEGIN
-- value is different, so do the update
UPDATE rt
SET rt.StringField = @NewValue
FROM dbo.Test rt
WHERE rt.ID = @ID;
END;
END;
परिणाम:
-- Transaction Log (0 entries):
Operation
----------------------------
-- SQL Profiler (2 Lock:Acquired events):
Mode Type
--------------------------------------
6 - IS 5 - OBJECT
6 - IS 6 - PAGE
इसलिए 3 के बजाय केवल 2 ताले प्राप्त किए गए हैं, और ये दोनों ही लॉक इन्टेंट शेयर किए गए हैं, इंटेंट एक्सक्लूसिव या इंटेंट अपडेट ( लॉक कम्पेटिबिलिटी ) नहीं। यह ध्यान में रखते हुए कि प्रत्येक लॉक अधिग्रहीत भी जारी हो जाएगा, प्रत्येक लॉक वास्तव में 2 ऑपरेशन है, इसलिए यह नई विधि मूल रूप से प्रस्तावित विधि में 6 संचालन के बजाय कुल 4 ऑपरेशन है। इस ऑपरेशन को ध्यान में रखते हुए हर 15 एमएस (लगभग, जैसा कि ओपी द्वारा कहा गया है) चल रहा है, यानी लगभग 66 बार प्रति सेकंड। इसलिए मूल प्रस्ताव प्रति सेकंड 396 लॉक / अनलॉक संचालन की मात्रा है, जबकि यह नई विधि केवल 264 लॉक / अनलॉक ऑपरेशन प्रति सेकंड भी हल्के-वजन वाले लॉक की मात्रा है। यह कमाल के प्रदर्शन की गारंटी नहीं है, लेकिन निश्चित रूप से परीक्षण के लायक है :-)।