2008-08-17 25 views
89

पर संग्रहीत प्रोसेस को सम्मिलित करें मैंने एक संग्रहित proc लिखा है जो एक रिकॉर्ड मौजूद होने पर अद्यतन करेगा, अन्यथा यह एक डालने वाला होगा। यह इस तरह दिखता है: इस तरह से यह लिखने के पीछेएसक्यूएल सर्वर

update myTable set [email protected], [email protected] where [email protected] 
if @@rowcount = 0 
insert into myTable (Col1, Col2) values (@col1, @col2) 

मेरे तर्क है कि अद्यतन एक अंतर्निहित जहां खंड का उपयोग कर चयन प्रदर्शन करेंगे और है कि अगर रिटर्न 0 तो डालने को होगा।

यह इस तरह से कर रही करने के लिए वैकल्पिक एक का चयन करें और फिर पंक्तियों की संख्या के आधार पर करने के लिए हो सकता है लौट आए या तो एक अद्यतन करते हैं या सम्मिलित होगा। यह मुझे अक्षम माना जाता है क्योंकि यदि आप एक अद्यतन करना चाहते हैं तो यह 2 चयन (पहला स्पष्ट चयन कॉल और अपडेट के दूसरे भाग में) का कारण बन जाएगा। यदि proc एक डालने के लिए किया गया था तो दक्षता में कोई अंतर नहीं होगा।

क्या मेरा तर्क ध्वनि यहाँ है? क्या यह एक संग्रहित proc में एक डालने और अद्यतन को गठबंधन करेगा?

उत्तर

56

आपका धारणा सही है, यह यह करने के लिए इष्टतम तरीका है और यह upsert/merge कहा जाता है।

Importance of UPSERT - from sqlservercentral.com:

ऊपर हम मेज से एक अतिरिक्त पढ़ने हटा रहे हैं उल्लेख किया है कि अगर हम Upsert बजाय का उपयोग मौजूद है मामले में हर अद्यतन के लिए। एक निवेशन, दोनों Upsert और अगर के लिए दुर्भाग्य से मौजूद है तरीकों एक ही नंबर का उपयोग मेज पर पढ़ता की। इसलिए अस्तित्व के लिए चेक केवल किया जाना चाहिए जब वहाँ अतिरिक्त आई/ओ का औचित्य साबित करने के लिए एक बहुत वैध कारण है। का अनुकूलित तरीका यह सुनिश्चित करना है कि डीबी पर जितना संभव हो उतना कम पढ़ा जाए।

सबसे अच्छी रणनीति अद्यतन प्रयास करने के लिए है। यदि अद्यतन से कोई पंक्ति प्रभावित नहीं होती है तो सम्मिलित करें। अधिकांश परिस्थितियों में, पंक्ति पहले से ही मौजूद होगी और केवल एक I/O आवश्यक होगी।

संपादित: कृपया बाहर this answer की जाँच करें और जुड़ा हुआ ब्लॉग पोस्ट इस पैटर्न और यह कैसे सुरक्षित काम करने के लिए के साथ समस्याओं के बारे में जानने के लिए।

+1

ठीक है, कम से कम एक प्रश्न का उत्तर दिया, मुझे लगता है। और मैंने कोड नहीं जोड़ा क्योंकि सवाल में कोड पहले से ही मेरे लिए सही लग रहा था। हालांकि मैं इसे एक लेनदेन में डालूंगा, लेकिन मैंने अद्यतन के लिए अलगाव स्तर को ध्यान में नहीं रखा। आपके जवाब में इंगित करने के लिए धन्यवाद! – binOr

6

एमईआरजीई एसक्यूएल सर्वर 2008 में नई सुविधाओं में से एक है, वैसे भी।

+0

और आपको इसका उपयोग इस हार्ड-टू-रीड होमब्रू बकवास के बजाय करना चाहिए। अच्छा उदाहरण यहां है - https://www.mssqltips.com/sqlservertip/1704/using-merge-in-sql-server-to-insert-update-and-delete-at-the-same-time/ –

8

हैं एसक्यूएल सर्वर 2000/2005 मूल कोड लेनदेन में संलग्न किया जा करने के लिए सुनिश्चित करें कि डेटा समवर्ती परिदृश्य में लगातार बने हुए हैं बनाने के लिए की जरूरत है के साथ प्रयोग की जाने वाली।

BEGIN TRANSACTION Upsert 
update myTable set [email protected], [email protected] where [email protected] 
if @@rowcount = 0 
insert into myTable (Col1, Col2) values (@col1, @col2) 
COMMIT TRANSACTION Upsert 

यह अतिरिक्त प्रदर्शन लागत लगेगा, लेकिन डेटा अखंडता सुनिश्चित करेगा।

जैसा कि पहले से ही सुझाया गया है, जोड़ें, जहां उपलब्ध हो वहां मेर्ज का उपयोग किया जाना चाहिए।

3

यूपीएसईआरटी का बड़ा प्रशंसक, वास्तव में प्रबंधित करने के लिए कोड पर कटौती करता है। यहां एक और तरीका है जो मैं करता हूं: इनपुट पैरामीटर में से एक आईडी है, यदि आईडी न्यूल या 0 है, तो आप जानते हैं कि यह एक INSERT है, अन्यथा यह एक अपडेट है।मान लें कि एप्लिकेशन जानता है कि कोई आईडी है या नहीं, इसलिए सभी परिस्थितियों में काम नहीं करेगा, लेकिन अगर आप ऐसा करते हैं तो निष्पादन में कटौती होगी।

1

आपका तर्क ध्वनि लगता है, लेकिन यदि आप किसी विशिष्ट प्राथमिक कुंजी में पास हुए हैं तो आप डालने से रोकने के लिए कुछ कोड जोड़ने पर विचार करना चाहेंगे।

अन्यथा, अगर आप किसी भी रिकॉर्ड को प्रभावित नहीं करते हैं तो अगर आप किसी भी रिकॉर्ड को प्रभावित नहीं करते हैं, तो क्या होता है, जब कोई "यूपीएसईआरटी" चलाने से पहले रिकॉर्ड हटा देता है तो क्या होता है? अब जो रिकॉर्ड आप अपडेट करने का प्रयास कर रहे थे वह मौजूद नहीं है, इसलिए यह इसके बजाय एक रिकॉर्ड बनाएगा। शायद वह व्यवहार नहीं है जिसे आप ढूंढ रहे थे।

3

आप एसक्यूएल 2008 में मर्ज कर नहीं रहे हैं, तो आप के लिए यह परिवर्तन करना होगा:

अगर @@ rowcount = 0 और @@ त्रुटि = 0

अन्यथा अद्यतन तो यह किसी कारण के लिए विफल रहता है बाद में डालने की कोशिश करेगा और एक सम्मिलित कथन पर पंक्ति गणना 0

5

आपको न केवल लेनदेन में इसे चलाने की आवश्यकता है, इसे उच्च अलगाव स्तर की भी आवश्यकता है। मैं वास्तव में डिफ़ॉल्ट अलगाव स्तर को पढ़ा जाता है और इस कोड को Serializable की आवश्यकता है।

SET transaction isolation level SERIALIZABLE 
BEGIN TRANSACTION Upsert 
UPDATE myTable set [email protected], [email protected] where [email protected] 
if @@rowcount = 0 
    begin 
    INSERT into myTable (ID, Col1, Col2) values (@ID @col1, @col2) 
    end 
COMMIT TRANSACTION Upsert 

शायद @@ त्रुटि जांच और रोलबैक भी जोड़ना अच्छा विचार हो सकता है।

+0

@ मनीष गोयल क्योंकि डेटाबेस में कई कमांड और प्राथमिकताएं पैराल में चलती हैं। फिर अद्यतन के बाद और सम्मिलित होने से पहले अन्य थ्रेड एक पंक्ति डाल सकता है। –

46

कृपया उपयोग किए जा सकने वाले अच्छे, सुरक्षित पैटर्न के लिए post on my blog पढ़ें। बहुत सारे विचार हैं, और इस प्रश्न पर स्वीकृत उत्तर सुरक्षित से बहुत दूर है।

त्वरित उत्तर के लिए निम्न पैटर्न आज़माएं। यह एसक्यूएल 2000 और उसके बाद ठीक काम करेगा। एसक्यूएल 2005 आपको त्रुटि हैंडलिंग देता है जो अन्य विकल्पों को खोलता है और एसक्यूएल 2008 आपको एक मर्ज कमांड देता है।

begin tran 
    update t with (serializable) 
    set hitCount = hitCount + 1 
    where pk = @id 
    if @@rowcount = 0 
    begin 
     insert t (pk, hitCount) 
     values (@id,1) 
    end 
commit tran 
+0

अपने ब्लॉग पोस्ट में आप अस्तित्व जांच में इन (अपडेटलॉक, सीरियलज़ेबल) संकेत का उपयोग करने के साथ निष्कर्ष निकालते हैं। हालांकि, एमएसडीएन पढ़ना राज्य कहता है: "अपडॉक - निर्दिष्ट करता है कि लेनदेन पूरा होने तक अद्यतन ताले लेना और आयोजित किया जाना चाहिए।" क्या इसका मतलब है कि धारावाहिक संकेत अधूरा है क्योंकि लेनदेन के बाकी हिस्सों के लिए अद्यतन लॉक आयोजित किया जाएगा, या क्या मैंने कुछ गलत समझा है? –

1

संशोधित दीमा मेलेंको पोस्ट:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 

BEGIN TRANSACTION UPSERT 

UPDATE MYTABLE 
SET COL1 = @col1, 
     COL2 = @col2 
WHERE ID = @ID 

IF @@rowcount = 0 
    BEGIN 
     INSERT INTO MYTABLE 
        (ID, 
        COL1, 
        COL2) 
     VALUES  (@ID, 
        @col1, 
        @col2) 
    END 

IF @@Error > 0 
    BEGIN 
     INSERT INTO MYERRORTABLE 
        (ID, 
        COL1, 
        COL2) 
     VALUES  (@ID, 
        @col1, 
        @col2) 
    END 

COMMIT TRANSACTION UPSERT 

आप जाल त्रुटि कर सकते हैं और एक असफल डालने मेज पर रिकॉर्ड भेजें।
मुझे ऐसा करने की ज़रूरत है क्योंकि हम डब्लूएसडीएल के माध्यम से जो भी डेटा भेज रहे हैं और यदि संभव हो तो इसे आंतरिक रूप से ठीक कर रहा है।

संबंधित मुद्दे