INSERT INTO <table>
SELECT <natural keys>, <other stuff...>
FROM <table>
WHERE NOT EXISTS
-- race condition risk here?
( SELECT 1 FROM <table> WHERE <natural keys> )
UPDATE ...
WHERE <natural keys>
- पहले INSERT में एक रेस कंडीशन होती है। कुंजी आंतरिक क्वेरी चयन के दौरान मौजूद नहीं हो सकती है, लेकिन INSERT समय पर मौजूद होती है जिसके परिणामस्वरूप कुंजी उल्लंघन होता है।
- INSERT और UPDATE के बीच एक रेस कंडीशन है। INSERT की आंतरिक क्वेरी में चेक किए जाने पर कुंजी मौजूद हो सकती है लेकिन UPDATE चलने के समय तक चली जाती है।
दूसरी दौड़ की स्थिति के लिए कोई यह तर्क दे सकता है कि समवर्ती धागे द्वारा कुंजी को वैसे भी हटा दिया गया होगा, इसलिए यह वास्तव में एक खोया हुआ अपडेट नहीं है।
इष्टतम समाधान आमतौर पर सबसे अधिक संभावना वाले मामले को आजमाने के लिए होता है, और यदि त्रुटि विफल हो जाती है (निश्चित रूप से लेनदेन के अंदर):
- यदि कुंजी गुम होने की संभावना है, तो हमेशा पहले डालें। अद्वितीय बाधा उल्लंघन को संभालें, अद्यतन करने के लिए वापस आएं।
- यदि कुंजी मौजूद होने की संभावना है, तो हमेशा पहले अपडेट करें। अगर कोई पंक्ति नहीं मिली तो डालें। संभव अद्वितीय बाधा उल्लंघन को संभालें, अद्यतन करने के लिए वापस आएं।
शुद्धता के अलावा, यह पैटर्न गति के लिए भी इष्टतम है:नकली लॉकअप करने की तुलना में अपवाद को सम्मिलित करने और संभालने का प्रयास करने के लिए अधिक कुशल है। लॉकअप का मतलब है लॉजिकल पेज रीड (जिसका मतलब फिजिकल पेज रीड हो सकता है), और आईओ (यहां तक कि लॉजिकल) SEH से ज्यादा महंगा है।
अपडेट करें @पीटर
एक भी कथन 'परमाणु' क्यों नहीं है? मान लें कि हमारे पास एक छोटी सी तालिका है:
create table Test (id int primary key);
अब अगर मैं इस सिंगल स्टेटमेंट को दो थ्रेड्स से एक लूप में चलाऊंगा, तो यह 'परमाणु' होगा, जैसा कि आप कहते हैं, कोई दौड़ की स्थिति मौजूद नहीं हो सकती है:
insert into Test (id)
select top (1) id
from Numbers n
where not exists (select id from Test where id = n.id);
फिर भी कुछ ही सेकंड में प्राथमिक कुंजी का उल्लंघन होता है:
<ब्लॉकक्वॉट>
Msg 2627, Level 14, State 1, Line 4
प्राथमिक कुंजी बाधा 'PK__Test__24927208' का उल्लंघन। ऑब्जेक्ट 'dbo.Test' में डुप्लीकेट कुंजी नहीं डाल सकते।
ऐसा क्यों है? आप सही हैं कि SQL क्वेरी योजना DELETE ... FROM ... JOIN
पर 'सही काम' करेगी। , WITH cte AS (SELECT...FROM ) DELETE FROM cte
और कई अन्य मामलों में। लेकिन इन मामलों में एक महत्वपूर्ण अंतर है:'सबक्वायरी' लक्ष्य . को संदर्भित करता है एक अपडेट . का या हटाएं कार्यवाही। ऐसे मामलों के लिए क्वेरी योजना वास्तव में एक उपयुक्त लॉक का उपयोग करेगी, वास्तव में मैं यह व्यवहार कुछ मामलों में महत्वपूर्ण है, जैसे कि कतारों को लागू करते समय तालिकाओं का उपयोग कतारों के रूप में करना।
लेकिन मूल प्रश्न में, साथ ही मेरे उदाहरण में, सबक्वेरी को क्वेरी ऑप्टिमाइज़र द्वारा एक क्वेरी में सबक्वेरी के रूप में देखा जाता है, न कि कुछ विशेष 'अपडेट के लिए स्कैन' प्रकार की क्वेरी के रूप में जिसे विशेष लॉक सुरक्षा की आवश्यकता होती है। नतीजा यह है कि सबक्वेरी लुकअप का निष्पादन एक समवर्ती पर्यवेक्षक द्वारा एक अलग ऑपरेशन के रूप में देखा जा सकता है , इस प्रकार कथन के 'परमाणु' व्यवहार को तोड़ता है। जब तक विशेष सावधानी नहीं बरती जाती है, एकाधिक थ्रेड समान मान डालने का प्रयास कर सकते हैं, दोनों आश्वस्त हैं कि उन्होंने जाँच की थी और मान पहले से मौजूद नहीं है। केवल एक ही सफल हो सकता है, दूसरा पीके उल्लंघन पर प्रहार करेगा। क्यूईडी.