SQL सर्वर पर INSERT या अद्यतन के लिए समाधान


596

की एक तालिका संरचना मान लें MyTable(KEY, datafield1, datafield2...)

अक्सर मैं या तो किसी मौजूदा रिकॉर्ड को अपडेट करना चाहता हूं, या अगर वह मौजूद नहीं है तो एक नया रिकॉर्ड डालें।

अनिवार्य रूप से:

IF (key exists)
  run update command
ELSE
  run insert command

इसे लिखने का सबसे अच्छा प्रदर्शन तरीका क्या है?



26
पहली बार इस प्रश्न पर आने वाले किसी भी व्यक्ति के लिए - कृपया सभी उत्तरों और उनकी टिप्पणियों को पढ़ना सुनिश्चित करें। उम्र कभी-कभी भ्रामक जानकारी दे सकती है ...
हारून बर्ट्रेंड

1
EXCEPT ऑपरेटर का उपयोग करने पर विचार करें, जो SQL Server 2005 में पेश किया गया था।
टार्ज़न

जवाबों:


370

लेन-देन के बारे में मत भूलना। प्रदर्शन अच्छा है, लेकिन सरल (IF EXTSTS ..) दृष्टिकोण बहुत खतरनाक है।
जब कई थ्रेड सम्मिलित-या-अद्यतन करने का प्रयास करेंगे, तो आप आसानी से प्राथमिक कुंजी उल्लंघन प्राप्त कर सकते हैं।

@ ब्यू क्रॉफोर्ड एंड @ इस्टेबन द्वारा प्रदान किए गए समाधान सामान्य विचार दिखाते हैं लेकिन त्रुटि-प्रवण।

गतिरोध और पीके के उल्लंघन से बचने के लिए आप कुछ इस तरह का उपयोग कर सकते हैं:

begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
   update table set ...
   where key = @key
end
else
begin
   insert into table (key, ...)
   values (@key, ...)
end
commit tran

या

begin tran
   update table with (serializable) set ...
   where key = @key

   if @@rowcount = 0
   begin
      insert into table (key, ...) values (@key,..)
   end
commit tran

सवाल सबसे सुरक्षित के बजाय सबसे अधिक प्रदर्शन करने वाले समाधान के लिए पूछा गया। जब भी कोई लेन-देन प्रक्रिया में सुरक्षा जोड़ता है, यह एक उपरि भी जोड़ता है।
ल्यूक बेनेट

31
ये दोनों तरीके अभी भी विफल हो सकते हैं। यदि दो समवर्ती धागे एक ही पंक्ति पर एक ही करते हैं, तो पहला सफल होगा, लेकिन प्राथमिक कुंजी उल्लंघन के कारण दूसरा सम्मिलित विफल हो जाएगा। लेन-देन की गारंटी नहीं है कि रिकॉर्ड अपडेट होने के बाद भी इन्सर्ट सफल हो जाएगा, क्योंकि रिकॉर्ड मौजूद था। यह गारंटी देने के लिए कि कोई भी समवर्ती लेन-देन सफल होगा, आपको लॉक का उपयोग करना होगा।
जीन विंसेंट

7
@aku किसी भी कारण से आपने अपने BEGIN TRAN से ठीक पहले "SET TRANSPORTION ISOLATION LEVEL SERIALIZABLE" के विपरीत टेबल संकेत ("xxxx)" का उपयोग किया?
EBAR

4
@CashCow, आखिरी जीत, यह वह है जो INSERT या UPDATE करने वाला है: पहला एक आवेषण, दूसरा रिकॉर्ड अपडेट करता है। लॉक जोड़ने से यह बहुत ही कम समय-सीमा में होता है, जिससे एक त्रुटि को रोका जा सकता है।
जीन विन्सेन्ट

1
मैंने हमेशा सोचा था कि लॉकिंग संकेत का उपयोग खराब है, और हमें Microsoft आंतरिक इंजन को ताले को हुक करने देना चाहिए। क्या यह नियम का स्पष्ट अपवाद है?

381

एक बहुत ही पिछले सवाल का मेरा विस्तृत जवाब देखें

@ ब्यू क्रॉफर्ड एसक्यूएल 2005 और उससे नीचे का एक अच्छा तरीका है, हालांकि यदि आप इसे वापस दे रहे हैं तो इसे एसओ के पहले नंबर पर जाना चाहिए। । एकमात्र समस्या यह है कि आवेषण के लिए यह अभी भी दो IO संचालन है।

MS Sql2008 mergeSQL से शुरू होता है: 2003 मानक:

merge tablename with(HOLDLOCK) as target
using (values ('new value', 'different value'))
    as source (field1, field2)
    on target.idfield = 7
when matched then
    update
    set field1 = source.field1,
        field2 = source.field2,
        ...
when not matched then
    insert ( idfield, field1, field2, ... )
    values ( 7,  source.field1, source.field2, ... )

अब यह वास्तव में सिर्फ एक IO ऑपरेशन है, लेकिन भयानक कोड :-(


10
@ इयान बॉयड - हाँ, यह SQL: 2003 मानक का सिंटैक्स है, न upsertकि इसके बारे में कि सभी अन्य DB प्रदाताओं ने इसके बजाय समर्थन करने का फैसला किया है। upsertयह यह केवल T-SQL में गैर मानक कीवर्ड है जैसे नहीं है - वाक्य रचना यह करने के लिए एक बहुत ही अच्छे तरह से तो बहुत कम एमएस में यह भी समर्थन किया है चाहिए,
कीथ

1
अन्य उत्तरों में ताला संकेत पर कोई टिप्पणी? (जल्द ही पता चल जाएगा, लेकिन अगर यह अनुशंसित तरीका है, तो मैं इसे उत्तर में जोड़ने की सलाह देता हूं)
एग्लियसियस

25
यहां देखें कि वेबलॉग्सSeqlteam.com/dang/archive/2009/01/31/… रेस की स्थिति को रोकने के लिए कैसे MERGEसिंटैक्स का उपयोग करते समय उत्पन्न होने वाली त्रुटियों को रोकने के लिए उत्तर के लिए ।
सिपाही

5
@Seph यह एक वास्तविक आश्चर्य है - Microsoft द्वारा कुछ हद तक विफल है: -एसआई का अनुमान है कि आपको HOLDLOCKउच्च संगामिति स्थितियों में मर्ज संचालन के लिए आवश्यक है।
कीथ

11
इस उत्तर को वास्तव में सेप द्वारा टिप्पणी के लिए खाते में अद्यतन करने की आवश्यकता है, क्योंकि यह बिना होल्डलॉक के थ्रेड-सुरक्षित नहीं है। लिंक की गई पोस्ट के अनुसार, MERGE ने एक अपडेट लॉक निकाला है, लेकिन पंक्तियों को सम्मिलित करने से पहले इसे जारी करता है, जो डालने पर दौड़ की स्थिति और प्राथमिक कुंजी उल्लंघन का कारण बन सकता है। HOLDLOCK का उपयोग करके, आवेषण होने के बाद तक ताले रखे जाते हैं।
त्रिवेंको

169

UPSERT करें:

अद्यतन MyTable सेट फ़ील्ड = @ फ़ील्ड की कुंजी = @ कुंजी

यदि @@ ROWCOUNT = 0 है
   INSERT INTO MyTable (FieldA) वाल्व (@FieldA)

http://en.wikipedia.org/wiki/Upsert


7
यदि आपके पास उचित अनन्य अनुक्रमणिका बाधाएँ हैं, तो प्राथमिक कुंजी उल्लंघन नहीं होने चाहिए। बाधा के पूरे बिंदु को हर होने से नकली पंक्तियों को रोकना है। इससे कोई फर्क नहीं पड़ता कि कितने धागे डालने की कोशिश कर रहे हैं, डेटाबेस बाधा को लागू करने के लिए आवश्यक रूप से अनुक्रमित करेगा ... और यदि ऐसा नहीं होता है, तो इंजन बेकार है। बेशक, इसे एक सीरियलाइज्ड ट्रांजैक्शन में लपेटने से यह गतिरोधों या असफल आवेषणों के लिए अधिक सही और कम संवेदनशील होगा।
त्रिंको

19
@Triynko, मुझे लगता है कि @Sam केसर का मतलब है कि अगर दो + धागे सही अनुक्रम में बिछा तो एसक्यूएल सर्वर होगा फेंक एक प्राथमिक कुंजी उल्लंघन का संकेत एक त्रुटि है | हुई। बयानों के उपरोक्त सेट में त्रुटियों को रोकने का एक सही तरीका है इसे एक धारावाहिक परिवर्तन में लपेटना।
२०:१२ बजे ईबर

1
यहां तक ​​कि अगर आपके पास एक प्राथमिक कुंजी है जो एक ऑटो-इन्क्रीमेंट है, तो आपकी चिंता तब कोई अनूठी बाधा होगी जो टेबल पर हो सकती है।
सिपाही

1
डेटाबेस को प्राथमिक प्रमुख मुद्दों का ध्यान रखना चाहिए। आप जो कह रहे हैं, वह यह है कि यदि अपडेट विफल हो जाता है और एक अन्य प्रक्रिया पहले सम्मिलित हो जाती है तो आपका इन्सर्ट विफल हो जाएगा। उस मामले में आप वैसे भी एक दौड़ की स्थिति है। लॉकिंग इस तथ्य को नहीं बदलेगा कि पोस्ट-कंडीशन यह होगी कि लिखने की कोशिश करने वाली प्रक्रियाओं में से एक को मूल्य मिलेगा।
कैशबैक

93

बहुत से लोग आपको उपयोग करने का सुझाव देंगे MERGE, लेकिन मैं आपको इसके प्रति सावधान करता हूं। डिफ़ॉल्ट रूप से, यह आपको कई बयानों की तुलना में संगति और दौड़ की स्थिति से बचाता नहीं है, लेकिन यह अन्य खतरों का परिचय देता है:

http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/

यहां तक ​​कि इस "सरल" सिंटैक्स उपलब्ध होने के बावजूद, मैं अभी भी इस दृष्टिकोण (त्रुटि से निपटने के लिए छोड़ी गई त्रुटि) को पसंद करता हूं:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE dbo.table SET ... WHERE PK = @PK;
IF @@ROWCOUNT = 0
BEGIN
  INSERT dbo.table(PK, ...) SELECT @PK, ...;
END
COMMIT TRANSACTION;

बहुत सारे लोग इस तरह से सुझाव देंगे:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
IF EXISTS (SELECT 1 FROM dbo.table WHERE PK = @PK)
BEGIN
  UPDATE ...
END
ELSE
  INSERT ...
END
COMMIT TRANSACTION;

लेकिन यह सब उपलब्धियां सुनिश्चित कर रही है कि अद्यतन किए जाने वाली पंक्ति का पता लगाने के लिए आपको तालिका को दो बार पढ़ने की आवश्यकता हो सकती है। पहले नमूने में, आपको केवल एक बार पंक्ति का पता लगाने की आवश्यकता होगी। (दोनों मामलों में, यदि प्रारंभिक रीड से कोई पंक्तियाँ नहीं मिली हैं, तो एक प्रविष्टि होती है।)

अन्य इस तरह से सुझाव देंगे:

BEGIN TRY
  INSERT ...
END TRY
BEGIN CATCH
  IF ERROR_NUMBER() = 2627
    UPDATE ...
END CATCH

हालाँकि, यह समस्याग्रस्त है अगर SQL सर्वर को अपवादों को छोड़ देने के अलावा और कोई कारण नहीं है जो आप पहली बार में रोक सकते थे, वह अधिक महंगा है, दुर्लभ परिदृश्य को छोड़कर जहां लगभग हर इंसर्ट विफल रहता है। मैं यहाँ जितना साबित करता हूँ:


3
मंदिर की तालिका से डालने / अपडेट करने के बारे में क्या है जो कई रिकॉर्ड सम्मिलित / अद्यतन करता है?
user960567

@ user960567 खैर,UPDATE target SET col = tmp.col FROM target INNER JOIN #tmp ON <key clause>; INSERT target(...) SELECT ... FROM #tmp AS t WHERE NOT EXISTS (SELECT 1 FROM target WHERE key = t.key);
हारून बर्ट्रेंड

4
2 से अधिक वर्षों के बाद अच्छा जवाब :)
user960567

12
@ user960567 क्षमा करें, मैं हमेशा वास्तविक समय में टिप्पणी सूचनाएं नहीं पकड़ता।
एरॉन बर्ट्रेंड

60
IF EXISTS (SELECT * FROM [Table] WHERE ID = rowID)
UPDATE [Table] SET propertyOne = propOne, property2 . . .
ELSE
INSERT INTO [Table] (propOne, propTwo . . .)

संपादित करें:

काश, यहां तक ​​कि अपने स्वयं के प्रतिबंध के लिए, मुझे उन समाधानों को स्वीकार करना चाहिए जो बिना किसी चयन के ऐसा करते हैं क्योंकि वे एक कम कदम के साथ कार्य को पूरा करते हैं।


6
मुझे अब भी यह बेहतर लगता है। मुखर को साइड इफ़ेक्ट द्वारा प्रोग्रामिंग करना अधिक पसंद है, और मैंने वास्तविक डेटाबेस में प्रदर्शन समस्याओं का कारण बनने के लिए उस प्रारंभिक चयन के पिडली थोड़ा संकुल सूचकांक कभी नहीं देखा है।
एरिक जेड बियर्ड

38

यदि आप एक बार में एक से अधिक रिकॉर्ड करना चाहते हैं तो आप ANSI SQL: 2003 DML स्टेटमेंट MERGE का उपयोग कर सकते हैं।

MERGE INTO table_name WITH (HOLDLOCK) USING table_name ON (condition)
WHEN MATCHED THEN UPDATE SET column1 = value1 [, column2 = value2 ...]
WHEN NOT MATCHED THEN INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...])

की जाँच करें SQL सर्वर 2005 में नकल करना मर्ज वक्तव्य


1
ओरेकल में, एक MERGE स्टेटमेंट जारी करके मुझे लगता है कि टेबल को लॉक कर दिया गया है। SQL * सर्वर में भी ऐसा ही होता है?
माइक मैक्लिस्टर

13
MERGE दौड़ की स्थिति के लिए अतिसंवेदनशील है ( जब तक आप इसे सर्टिफिकेट लॉक नहीं बनाते हैं, वेबलॉग .qqlteam.com/dang/archive/2009/01/31/…… देखें )। इसके अलावा, SQL Profiler में MERGE के प्रदर्शन पर एक नज़र डालें ... मुझे लगता है कि यह टाइप करने में धीमा है और वैकल्पिक समाधानों की तुलना में अधिक रीड उत्पन्न करता है।
EBAR

@EBarr - ताले पर लिंक के लिए धन्यवाद। सुझाव लॉक करने के सुझाव को शामिल करने के लिए मैंने अपना उत्तर अपडेट कर दिया है।
एरिक वील्नाउ


10

हालाँकि इसकी टिप्पणी करने में बहुत देर हो गई है लेकिन मैं MERGE का उपयोग करके एक और पूर्ण उदाहरण जोड़ना चाहता हूं।

इस तरह के इन्सर्ट + अपडेट स्टेटमेंट को आमतौर पर "अपग्रेड" स्टेटमेंट्स कहा जाता है और इसे SQL सर्वर में MERGE का उपयोग करके लागू किया जा सकता है।

एक बहुत अच्छा उदाहरण यहाँ दिया गया है: http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx

ऊपर बताया गया है कि लॉकिंग और कंसीडर परिदृश्य भी।

मैं संदर्भ के लिए उसी को उद्धृत करूंगा:

ALTER PROCEDURE dbo.Merge_Foo2
      @ID int
AS

SET NOCOUNT, XACT_ABORT ON;

MERGE dbo.Foo2 WITH (HOLDLOCK) AS f
USING (SELECT @ID AS ID) AS new_foo
      ON f.ID = new_foo.ID
WHEN MATCHED THEN
    UPDATE
            SET f.UpdateSpid = @@SPID,
            UpdateTime = SYSDATETIME()
WHEN NOT MATCHED THEN
    INSERT
      (
            ID,
            InsertSpid,
            InsertTime
      )
    VALUES
      (
            new_foo.ID,
            @@SPID,
            SYSDATETIME()
      );

RETURN @@ERROR;

1
MERGE के बारे में चिंता करने के लिए अन्य चीजें हैं: mssqltips.com/sqlservertip/3074/…
हारून बर्ट्रेंड

8
/*
CREATE TABLE ApplicationsDesSocietes (
   id                   INT IDENTITY(0,1)    NOT NULL,
   applicationId        INT                  NOT NULL,
   societeId            INT                  NOT NULL,
   suppression          BIT                  NULL,
   CONSTRAINT PK_APPLICATIONSDESSOCIETES PRIMARY KEY (id)
)
GO
--*/

DECLARE @applicationId INT = 81, @societeId INT = 43, @suppression BIT = 0

MERGE dbo.ApplicationsDesSocietes WITH (HOLDLOCK) AS target
--set the SOURCE table one row
USING (VALUES (@applicationId, @societeId, @suppression))
    AS source (applicationId, societeId, suppression)
    --here goes the ON join condition
    ON target.applicationId = source.applicationId and target.societeId = source.societeId
WHEN MATCHED THEN
    UPDATE
    --place your list of SET here
    SET target.suppression = source.suppression
WHEN NOT MATCHED THEN
    --insert a new line with the SOURCE table one row
    INSERT (applicationId, societeId, suppression)
    VALUES (source.applicationId, source.societeId, source.suppression);
GO

तालिका और फ़ील्ड नामों को आपकी ज़रूरत के अनुसार बदलें। ऑन का उपयोग करने का ख्याल रखें कंडीशन । फिर DECLARE लाइन पर चर के लिए उचित मूल्य (और प्रकार) सेट करें।

चीयर्स।


7

आप MERGEस्टेटमेंट का उपयोग कर सकते हैं , इस स्टेटमेंट का उपयोग डेटा को डालने के लिए किया जाता है यदि मौजूद नहीं है या यदि मौजूद है तो अपडेट करें।

MERGE INTO Employee AS e
using EmployeeUpdate AS eu
ON e.EmployeeID = eu.EmployeeID`

@RamenChef मुझे समझ नहीं आया। कहाँ से जुड़े खंड हैं?
२०:४५ बजे जैसे

@likejudo मैंने यह नहीं लिखा; मैंने केवल इसे संशोधित किया। पोस्ट लिखने वाले उपयोगकर्ता से पूछें।
RamenChef

5

यदि कोई अद्यतन नहीं-पंक्तियों को अद्यतन किया जा रहा है, तो INSERT मार्ग पर जा रहे हैं, तो रेस की स्थिति को रोकने के लिए पहले INSERT करने पर विचार करें (कोई हस्तक्षेप DELETE नहीं मानते हुए)

INSERT INTO MyTable (Key, FieldA)
   SELECT @Key, @FieldA
   WHERE NOT EXISTS
   (
       SELECT *
       FROM  MyTable
       WHERE Key = @Key
   )
IF @@ROWCOUNT = 0
BEGIN
   UPDATE MyTable
   SET FieldA=@FieldA
   WHERE Key=@Key
   IF @@ROWCOUNT = 0
   ... record was deleted, consider looping to re-run the INSERT, or RAISERROR ...
END

दौड़ की स्थिति से बचने के अलावा, अगर ज्यादातर मामलों में रिकॉर्ड पहले से मौजूद होगा, तो यह INSERT को विफल कर देगा, CPU को बर्बाद कर देगा।

शायद SQL2008 के लिए बाद में बेहतर का उपयोग करना।


दिलचस्प विचार, लेकिन गलत वाक्यविन्यास। SELECT को FROM <table_source>, और TOP 1 की जरूरत है (जब तक कि चुनी गई table_source की केवल 1 पंक्ति न हो)।
19

धन्यवाद। मैंने इसे बदल कर एक नहीं के बराबर कर दिया है। ओ / पी के अनुसार "की" के लिए परीक्षण के कारण केवल एक मिलान पंक्ति होगी (हालाँकि यह एक बहु-भाग कुंजी होने की आवश्यकता हो सकती है :))
क्रिस्टन

4

यह उपयोग पैटर्न पर निर्भर करता है। एक विवरण में खो जाने के बिना उपयोग बड़ी तस्वीर को देखना है। उदाहरण के लिए, यदि रिकॉर्ड बनाने के बाद उपयोग पैटर्न 99% अपडेट है, तो 'UPSERT' सबसे अच्छा समाधान है।

पहले इंसर्ट (हिट) के बाद, यह सभी एकल स्टेटमेंट अपडेट होंगे, कोई इफ़्स या बट्स नहीं। सम्मिलित करने पर 'जहां' स्थिति आवश्यक है अन्यथा यह डुप्लिकेट सम्मिलित करेगा, और आप लॉकिंग से निपटना नहीं चाहते हैं।

UPDATE <tableName> SET <field>=@field WHERE key=@key;

IF @@ROWCOUNT = 0
BEGIN
   INSERT INTO <tableName> (field)
   SELECT @field
   WHERE NOT EXISTS (select * from tableName where key = @key);
END

2

MS SQL Server 2008 MERGE स्टेटमेंट को प्रस्तुत करता है, जो मेरा मानना ​​है कि SQL: 2003 मानक का हिस्सा है। जैसा कि कई लोगों ने दिखाया है कि एक पंक्ति के मामलों को संभालना कोई बड़ी बात नहीं है, लेकिन बड़े डेटासेट के साथ काम करते समय, एक कर्सर की जरूरत होती है, जिसमें सभी प्रदर्शन समस्याएं होती हैं। बड़े डेटासेट के साथ काम करते समय MERGE स्टेटमेंट का बहुत स्वागत किया जाएगा।


1
मुझे बड़े डेटासेट के साथ ऐसा करने के लिए कर्सर का उपयोग करने की आवश्यकता नहीं है। आपको बस एक अद्यतन की आवश्यकता है जो रिकॉर्ड से मेल खाता है और एक मान खंड के बजाय एक चयन के साथ सम्मिलित करता है जो तालिका में शामिल हो जाता है।
HLGEM

1

इससे पहले कि हर कोई सीधे अपने sprocs चल रहे इन nafarious उपयोगकर्ताओं से बाहर रखने के लिए HOLDLOCK-s से कूदता है :-) मुझे बताना होगा कि आपको डिज़ाइन द्वारा नए PK-s की विशिष्टता की गारंटी देनी होगी (पहचान की चाबियाँ, ओरेकल में अनुक्रम जनरेटर, के लिए अद्वितीय अनुक्रमित बाहरी आईडी-एस, इंडेक्स द्वारा कवर किए गए प्रश्न)। यह समस्या का अल्फा और ओमेगा है। यदि आपके पास ऐसा नहीं है, तो ब्रह्मांड का कोई भी होल्ड-लॉक आपको बचाने के लिए नहीं जा रहा है और यदि आपके पास ऐसा है, तो आपको पहले चयन (या अपडेट का उपयोग करने के लिए) पर UPDLOCK से आगे कुछ भी करने की आवश्यकता नहीं है।

स्प्रोक्स सामान्य रूप से बहुत नियंत्रित परिस्थितियों में और एक विश्वसनीय कॉलर (मध्य स्तरीय) की धारणा के साथ चलते हैं। इसका मतलब है कि अगर एक साधारण अपचर पैटर्न (अपडेट + इंसर्ट या मर्ज) कभी डुप्लिकेट पीके देखता है, जिसका मतलब है कि आपके मध्य स्तरीय या टेबल डिज़ाइन में बग है और यह अच्छा है कि एसक्यूएल ऐसे मामले में गलती करेगा और रिकॉर्ड को अस्वीकार कर देगा। इस मामले में एक HOLDLOCK रखने से अपवाद खाने और अपने दोष को कम करने के अलावा संभावित दोषपूर्ण डेटा में लेने के बराबर है।

ऐसा होने के बाद, MERGE, या UPDATE का उपयोग करना तब INSERT आपके सर्वर पर कम आसान है और कम त्रुटि प्रवण है क्योंकि आपको पहले चयन करने के लिए (UPDLOCK) को जोड़ना याद नहीं है। इसके अलावा, यदि आप छोटे बैचों में आवेषण / अपडेट कर रहे हैं, तो आपको यह तय करने के लिए अपने डेटा को जानना होगा कि क्या लेनदेन उचित है या नहीं। यह असंबंधित अभिलेखों का एक संग्रह है, फिर अतिरिक्त "लिफाफा" लेनदेन हानिकारक होगा।


1
यदि आप सिर्फ एक अपडेट करते हैं तो बिना किसी लॉकिंग या एलिवेटेड आइसोलेशन के डालें, तो दो उपयोगकर्ता एक ही डेटा को वापस भेजने की कोशिश कर सकते हैं (यदि मैं दो उपयोगकर्ताओं द्वारा सटीक एक ही जानकारी सबमिट करने का प्रयास करता हूं, तो मैं इसे मध्य स्तरीय में बग नहीं मानूंगा। एक ही समय - संदर्भ पर बहुत कुछ निर्भर करता है, है ना?)। वे दोनों अपडेट दर्ज करते हैं, जो दोनों के लिए 0 पंक्तियों को वापस करता है, फिर वे दोनों सम्मिलित करने का प्रयास करते हैं। एक जीतता है, दूसरे को अपवाद मिलता है। यह वही है जो लोग आमतौर पर बचने की कोशिश कर रहे हैं।
हारून बर्ट्रेंड

1

यदि आप पहली बार एक प्रविष्टि के बाद एक अद्यतन की कोशिश करते हैं तो क्या दौड़ की स्थिति वास्तव में मायने रखती है? कहते हैं कि आपके पास दो धागे हैं जो मुख्य कुंजी के लिए एक मूल्य निर्धारित करना चाहते हैं :

थ्रेड 1: मान = 1
थ्रेड 2: मान = 2

उदाहरण दौड़ हालत परिदृश्य

  1. कुंजी परिभाषित नहीं है
  2. थ्रेड 1 अद्यतन के साथ विफल रहता है
  3. थ्रेड 2 अद्यतन के साथ विफल रहता है
  4. वास्तव में थ्रेड 1 या थ्रेड 2 में से एक सम्मिलित के साथ सफल होता है। जैसे धागा १
  5. अन्य थ्रेड सम्मिलित करने में विफल रहता है (त्रुटि डुप्लिकेट कुंजी के साथ) - थ्रेड 2।

    • परिणाम: सम्मिलित करने के लिए दो टुकड़ों के "पहले", मूल्य तय करता है।
    • वांटेड परिणाम: डेटा लिखने के लिए 2 थ्रेड्स में से अंतिम (अपडेट या इंसर्ट) मूल्य तय करना चाहिए

परंतु; एक बहुस्तरीय वातावरण में, ओएस अनुसूचक धागा निष्पादन के क्रम पर निर्णय लेता है - उपरोक्त परिदृश्य में, जहां हमारे पास यह दौड़ की स्थिति है, यह ओएस था जिसने निष्पादन के अनुक्रम पर निर्णय लिया था। Ie: यह कहना गलत है कि "थ्रेड 1" या "थ्रेड 2" एक सिस्टम दृष्टिकोण से "पहला" था।

जब निष्पादन का समय थ्रेड 1 और थ्रेड 2 के करीब होता है, तो रेस की स्थिति का परिणाम कोई फर्क नहीं पड़ता। केवल आवश्यकता यह होनी चाहिए कि थ्रेड्स में से एक परिणामी मान को परिभाषित करे।

कार्यान्वयन के लिए: यदि त्रुटि "डुप्लिकेट कुंजी" में परिणाम सम्मिलित करने के बाद अद्यतन किया जाता है, तो इसे सफलता माना जाना चाहिए।

इसके अलावा, एक निश्चित रूप से यह मान कभी नहीं करना चाहिए डेटाबेस में है कि मूल्य मूल्य आप पिछले लिखा के समान है।


1

SQL Server 2008 में आप MERGE कथन का उपयोग कर सकते हैं


11
यह एक टिप्पणी है। किसी भी वास्तविक उदाहरण कोड के अभाव में यह साइट पर कई अन्य टिप्पणियों की तरह है।
स्वैसेक

बहुत पुराना है, लेकिन एक उदाहरण अच्छा होगा।
मैट मैककेबे

0

मैंने नीचे समाधान की कोशिश की थी और यह मेरे लिए काम करता है, जब सम्मिलित बयान के लिए समवर्ती अनुरोध होता है।

begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
   update table set ...
   where key = @key
end
else
begin
   insert table (key, ...)
   values (@key, ...)
end
commit tran

0

आप इस क्वेरी का उपयोग कर सकते हैं। सभी SQL सर्वर संस्करणों में कार्य करें। यह सरल है, और स्पष्ट है। लेकिन आपको 2 प्रश्नों का उपयोग करने की आवश्यकता है। आप उपयोग कर सकते हैं यदि आप MERGE का उपयोग नहीं कर सकते हैं

    BEGIN TRAN

    UPDATE table
    SET Id = @ID, Description = @Description
    WHERE Id = @Id

    INSERT INTO table(Id, Description)
    SELECT @Id, @Description
    WHERE NOT EXISTS (SELECT NULL FROM table WHERE Id = @Id)

    COMMIT TRAN

नोट: कृपया उत्तर नकारात्मक बताएं


मैं ताला लगाने की कमी का अनुमान लगा रहा हूं?
Zeek2

कोई कमी नहीं ताला ... मैं "TRAN" का उपयोग करें। डिफ़ॉल्ट sql- सर्वर लेनदेन में लॉकिंग है।
विक्टर सांचेज़

-2

यदि आप ADO.NET का उपयोग करते हैं, तो DataAdapter इसे संभालता है।

यदि आप इसे स्वयं संभालना चाहते हैं, तो यह तरीका है:

सुनिश्चित करें कि आपके मुख्य स्तंभ पर एक प्राथमिक कुंजी बाधा है।

फिर आप:

  1. अद्यतन करें
  2. यदि अद्यतन विफल रहता है क्योंकि कुंजी के साथ एक रिकॉर्ड पहले से मौजूद है, तो सम्मिलित करें। यदि अद्यतन विफल नहीं होता है, तो आप समाप्त कर रहे हैं।

आप इसे दूसरे तरीके से राउंड भी कर सकते हैं, अर्थात पहले इन्सर्ट कर सकते हैं, और इन्सर्ट विफल होने पर अपडेट करें। आम तौर पर पहला तरीका बेहतर होता है, क्योंकि अपडेट आवेषण की तुलना में अधिक बार किए जाते हैं।


... और पहले सम्मिलित करना (यह जानते हुए कि यह कभी-कभी विफल हो जाएगा) SQL सर्वर के लिए महंगा है। sqlperformance.com/2012/08/t-sql-queries/error-handling
हारून बर्ट्रेंड

-3

यदि एक मौजूद है ... और ... दो अनुरोधों को करना शामिल है (एक की जाँच करने के लिए, एक कार्रवाई करने के लिए)। निम्नलिखित दृष्टिकोण के लिए केवल उसी जगह की आवश्यकता होती है जहां रिकॉर्ड मौजूद है, दो यदि एक प्रविष्टि आवश्यक है:

DECLARE @RowExists bit
SET @RowExists = 0
UPDATE MyTable SET DataField1 = 'xxx', @RowExists = 1 WHERE Key = 123
IF @RowExists = 0
  INSERT INTO MyTable (Key, DataField1) VALUES (123, 'xxx')

-3

मैं आमतौर पर अन्य पोस्टरों में से कई को इसके लिए जाँच करने के संबंध में कहता हूँ जो पहले मौजूद थे और फिर जो भी सही रास्ता है, करने के लिए। ऐसा करते समय आपको एक बात याद रखनी चाहिए कि sql द्वारा कैश की गई निष्पादन योजना एक रास्ते या दूसरे के लिए नॉनटॉफिल हो सकती है। मेरा मानना ​​है कि ऐसा करने का सबसे अच्छा तरीका दो अलग-अलग संग्रहीत प्रक्रियाओं को कॉल करना है।

FirstSP:
अगर अस्तित्व
   दूसरा कॉल करें (UpdateProc)
अन्य
   तीसरा कॉल करें (InsertProc)

अब, मैं अपनी सलाह का पालन अक्सर नहीं करता, इसलिए इसे नमक के दाने के साथ लें।


यह SQL सर्वर के पुराने संस्करणों में प्रासंगिक हो सकता है, लेकिन आधुनिक संस्करणों में कथन-स्तर का संकलन है। फोर्क्स आदि कोई मुद्दा नहीं है, और इन चीजों के लिए अलग-अलग प्रक्रियाओं का उपयोग करने से किसी भी तरह से अपडेट और इंसर्ट के बीच चुनाव करने में निहित किसी भी समस्या का समाधान नहीं होता है ...
हारून बर्ट्रेंड

-10

एक चयन करें, यदि आपको कोई परिणाम मिलता है, तो इसे अपडेट करें, यदि नहीं, तो इसे बनाएं।


3
यह डेटाबेस के लिए दो कॉल है।
क्रिस कूडमोर

3
मुझे इसमें कोई समस्या नहीं दिख रही है।
क्लिंट ईकर सेप

10
यह DB की दो कॉल है जो समस्या है, आप अंत में DB को राउंडट्रिप्स की संख्या को दोगुना करते हैं। अगर एप्लिकेशन बहुत सारे आवेषण / अद्यतनों के साथ db को हिट करता है तो यह प्रदर्शन को चोट पहुंचाएगा। यूपीएसईआरटी एक बेहतर रणनीति है।
केव

5
यह भी एक दौड़ हालत नहीं बनाता है?
नीको
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.