শর্তসাপেক্ষ INSERT এবং নির্বাচন তুলনায় OUTPUT সহ একটি মার্জ কি ভাল অনুশীলন?


12

আমরা প্রায়শই "যদি উপস্থিত না থাকে তবে সন্নিবেশ করান" পরিস্থিতির মুখোমুখি হই। ড্যান গুজম্যানের ব্লগে এই প্রক্রিয়াটি থ্রেডসেফ কীভাবে করা যায় তার একটি দুর্দান্ত তদন্ত রয়েছে।

আমার কাছে একটি বেসিক টেবিল রয়েছে যা কেবল একটি থেকে কোনও পূর্ণসংখ্যার সাথে স্ট্রিংটি তালিকাভুক্ত করে SEQUENCE। একটি সঞ্চিত পদ্ধতিতে আমার হয় মানটির উপস্থিতি থাকলে পূর্ণসংখ্যা কীটি পাওয়া দরকার, অথবা INSERTএটি এবং তারপরে ফলাফলটি পাওয়া উচিত। dbo.NameLookup.ItemNameকলামটিতে একটি স্বতন্ত্রতা বাধা রয়েছে তাই ডেটা অখণ্ডতা ঝুঁকিপূর্ণ না হলেও আমি ব্যতিক্রমগুলির মুখোমুখি হতে চাই না।

এটি এমন নয় IDENTITYযাতে আমি পেতে পারি না SCOPE_IDENTITYএবং মান NULLকিছু ক্ষেত্রে হতে পারে ।

আমার পরিস্থিতিতে আমাকে কেবল INSERTটেবিলের নিরাপত্তার সাথে মোকাবিলা করতে হবে তাই আমি সিদ্ধান্ত নেওয়ার চেষ্টা করছি যে এটির MERGEমতো ব্যবহারের জন্য এটি আরও ভাল অনুশীলন :

SET NOCOUNT, XACT_ABORT ON;

DECLARE @vValueId INT 
DECLARE @inserted AS TABLE (Id INT NOT NULL)

MERGE 
    dbo.NameLookup WITH (HOLDLOCK) AS f 
USING 
    (SELECT @vName AS val WHERE @vName IS NOT NULL AND LEN(@vName) > 0) AS new_item
        ON f.ItemName= new_item.val
WHEN MATCHED THEN
    UPDATE SET @vValueId = f.Id
WHEN NOT MATCHED BY TARGET THEN
    INSERT
      (ItemName)
    VALUES
      (@vName)
OUTPUT inserted.Id AS Id INTO @inserted;
SELECT @vValueId = s.Id FROM @inserted AS s

আমি ব্যবহার না করেই এই কাজ করতে পারে MERGEশুধু একটি শর্তাধীন সঙ্গে INSERTএকটি দ্বারা অনুসরণ SELECT আমি মনে করি যে এই দ্বিতীয় পদ্ধতির পাঠক পরিষ্কার, কিন্তু আমি এটা "better" বা অনুশীলন প্রতীত করছি না

SET NOCOUNT, XACT_ABORT ON;

INSERT INTO 
    dbo.NameLookup (ItemName)
SELECT
    @vName
WHERE
    NOT EXISTS (SELECT * FROM dbo.NameLookup AS t WHERE @vName IS NOT NULL AND LEN(@vName) > 0 AND t.ItemName = @vName)

DECLARE @vValueId int;
SELECT @vValueId = i.Id FROM dbo.NameLookup AS i WHERE i.ItemName = @vName

অথবা সম্ভবত আর একটি ভাল উপায় আছে যা আমি বিবেচনা করি নি

আমি অন্যান্য প্রশ্নগুলি অনুসন্ধান এবং রেফারেন্স করেছি। এটির মধ্যে: /programming/5288283/sql-server-insert-if-not-exists-best- অনুশীলনটি আমি সবচেয়ে উপযুক্ত খুঁজে পেয়েছি তবে আমার ব্যবহারের ক্ষেত্রে এটি খুব বেশি প্রযোজ্য বলে মনে হচ্ছে না। IF NOT EXISTS() THENপদ্ধতির অন্যান্য প্রশ্ন যা আমি মনে করি না এটি গ্রহণযোগ্য।


আপনি কি আপনার বাফারের চেয়ে বড় সারণীগুলির সাথে পরীক্ষার চেষ্টা করেছেন, আমার এমন অভিজ্ঞতা হয়েছে যেখানে টেবিলটি নির্দিষ্ট আকারে আঘাত হ্রাসের পরে একত্রীকরণের কর্মক্ষমতা বন্ধ হয়ে যায়।
প্রশান্তভাবে

উত্তর:


8

যেহেতু আপনি একটি সিকোয়েন্স ব্যবহার করছেন, আপনি ফাংশনের জন্য একই নেক্সট ভ্যালুটি ব্যবহার করতে পারেন - যেটি আপনার আগে থেকেই Idপ্রাথমিক কী ক্ষেত্রে একটি ডিফল্ট সীমাবদ্ধতায় রয়েছে - Idসময়ের আগে একটি নতুন মান উত্পন্ন করতে । প্রথমে মান উত্পন্ন করার অর্থ হ'ল আপনাকে না থাকার বিষয়ে চিন্তা করার দরকার নেই SCOPE_IDENTITY, যার অর্থ হল নতুন মান পাওয়ার জন্য আপনাকে আর OUTPUTধারা বা প্রয়োজনের অতিরিক্ত SELECTদরকার নেই; আপনি এটি করার আগে আপনার মূল্য হবে INSERTএবং আপনাকে এমনকি SET IDENTITY INSERT ON / OFF:-) নিয়ে গণ্ডগোলের দরকার নেই

সুতরাং এটি সামগ্রিক পরিস্থিতির একটি অংশের যত্ন নেয়। অন্য অংশটি হ'ল দুটি প্রক্রিয়াটির সামঞ্জস্য ইস্যুটি হুবহু একই সময়ে, সঠিক একই স্ট্রিংয়ের জন্য বিদ্যমান সারিটি খুঁজে না পাওয়া এবং এর সাথে এগিয়ে চলছে INSERT। উদ্বেগটি হ'ল ইউনিক সীমাবদ্ধতা লঙ্ঘন এড়ানো সম্পর্কে।

এই ধরণের সম্মতি সংক্রান্ত সমস্যাগুলি হ্যান্ডেল করার একটি উপায় হল এই নির্দিষ্ট ক্রিয়াকে একক থ্রেড করাতে বাধ্য করা। এটি করার উপায় হ'ল অ্যাপ্লিকেশন লকগুলি (যা অধিবেশন জুড়ে কাজ করে) ব্যবহার করে। কার্যকর থাকাকালীন, তারা এ জাতীয় পরিস্থিতির জন্য কিছুটা ভারী হাতের হতে পারে যেখানে সংঘর্ষের ফ্রিকোয়েন্সি সম্ভবত মোটামুটি কম।

সংঘর্ষগুলির সাথে মোকাবিলা করার অন্য উপায়টি হ'ল এটিকে এড়াতে চেষ্টা করার চেয়ে তারা কখনও কখনও ঘটবে এবং তাদের পরিচালনা করবে তা গ্রহণ করা। TRY...CATCHকনস্ট্রাক্টটি ব্যবহার করে আপনি কার্যকরভাবে একটি নির্দিষ্ট ত্রুটি আটকে রাখতে পারেন (এই ক্ষেত্রে: "অনন্য প্রতিবন্ধকতা লঙ্ঘন", এমএসজি ২0০১) এবং মানটি SELECTপেতে পুনরায় সম্পাদন করতে পারেন Idযেহেতু আমরা জানি যে এটি বর্তমানে CATCHসেই নির্দিষ্ট সাথে ব্লকের মধ্যে থাকার কারণে রয়েছে know ত্রুটি. অন্যান্য ত্রুটিগুলি সাধারণত RAISERROR/ RETURNবা THROWপদ্ধতিতে পরিচালনা করা যায়।

পরীক্ষার সেটআপ: সিকোয়েন্স, সারণী এবং অনন্য সূচি

USE [tempdb];

CREATE SEQUENCE dbo.MagicNumber
  AS INT
  START WITH 1
  INCREMENT BY 1;

CREATE TABLE dbo.NameLookup
(
  [Id] INT NOT NULL
         CONSTRAINT [PK_NameLookup] PRIMARY KEY CLUSTERED
        CONSTRAINT [DF_NameLookup_Id] DEFAULT (NEXT VALUE FOR dbo.MagicNumber),
  [ItemName] NVARCHAR(50) NOT NULL         
);

CREATE UNIQUE NONCLUSTERED INDEX [UIX_NameLookup_ItemName]
  ON dbo.NameLookup ([ItemName]);
GO

পরীক্ষা সেটআপ: সঞ্চিত পদ্ধতি

CREATE PROCEDURE dbo.GetOrInsertName
(
  @SomeName NVARCHAR(50),
  @ID INT OUTPUT,
  @TestRaceCondition BIT = 0
)
AS
SET NOCOUNT ON;

BEGIN TRY
  SELECT @ID = nl.[Id]
  FROM   dbo.NameLookup nl
  WHERE  nl.[ItemName] = @SomeName
  AND    @TestRaceCondition = 0;

  IF (@ID IS NULL)
  BEGIN
    SET @ID = NEXT VALUE FOR dbo.MagicNumber;

    INSERT INTO dbo.NameLookup ([Id], [ItemName])
    VALUES (@ID, @SomeName);
  END;
END TRY
BEGIN CATCH
  IF (ERROR_NUMBER() = 2601) -- "Cannot insert duplicate key row in object"
  BEGIN
    SELECT @ID = nl.[Id]
    FROM   dbo.NameLookup nl
    WHERE  nl.[ItemName] = @SomeName;
  END;
  ELSE
  BEGIN
    ;THROW; -- SQL Server 2012 or newer
    /*
    DECLARE @ErrorNumber INT = ERROR_NUMBER(),
            @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE();

    RAISERROR(N'Msg %d: %s', 16, 1, @ErrorNumber, @ErrorMessage);
    RETURN;
    */
  END;

END CATCH;
GO

পরীক্ষা

DECLARE @ItemID INT;
EXEC dbo.GetOrInsertName
  @SomeName = N'test1',
  @ID = @ItemID OUTPUT;
SELECT @ItemID AS [ItemID];
GO

DECLARE @ItemID INT;
EXEC dbo.GetOrInsertName
  @SomeName = N'test1',
  @ID = @ItemID OUTPUT,
  @TestRaceCondition = 1;
SELECT @ItemID AS [ItemID];
GO

ওপি থেকে প্রশ্ন

কেন এই চেয়ে ভাল MERGE? ধারাটি TRYব্যবহার না করে আমি কি একই কার্যকারিতা পাব না WHERE NOT EXISTS?

MERGEবিভিন্ন "ইস্যু" রয়েছে (বেশ কয়েকটি তথ্যসূত্র @ এসকিউএলজিমের উত্তরে লিঙ্কযুক্ত তাই এখানে তথ্যটি নকল করার প্রয়োজন নেই)। এবং, এই পদ্ধতির কোনও অতিরিক্ত লকিং নেই (কম বিতর্ক), সুতরাং এটি একযোগে ভাল হওয়া উচিত। এই পদ্ধতির ক্ষেত্রে আপনি কখনই কোনও অনন্য বাধা লঙ্ঘন পাবেন না HOLDLOCK, ইত্যাদি It's এটি কাজ করার পক্ষে যথেষ্ট গ্যারান্টিযুক্ত।

এই পদ্ধতির পিছনে যুক্তিটি হ'ল:

  1. আপনার যদি এই পদ্ধতির পর্যাপ্ত মৃত্যুদন্ড কার্যকর হয় যাতে আপনার সংঘর্ষের বিষয়ে চিন্তা করা দরকার, তবে আপনি চান না:
    1. প্রয়োজনের চেয়ে আরও বেশি পদক্ষেপ গ্রহণ করুন
    2. প্রয়োজনের চেয়ে বেশি সময় ধরে কোনও সংস্থানগুলিতে লক ধরে রাখুন
  2. যেহেতু সংঘর্ষগুলি কেবলমাত্র নতুন এন্ট্রিগুলিতেই ঘটতে পারে ( ঠিক একই সময়ে নতুন এন্ট্রি দাখিল করা হয় ), CATCHতাই প্রথম স্থানে ব্লকের মধ্যে পড়ার ফ্রিকোয়েন্সি বেশ কম হবে। কোডটি অপ্টিমাইজ করতে আরও বেশি অর্থবোধ তৈরি করে যা কোডের পরিবর্তে 99% সময় চালাবে যা 1% সময় চলবে (যদি না উভয়কেই অনুকূল করে তোলার জন্য কোনও ব্যয় না হয় তবে এখানে এটি হয় না)।

@ এসকিএলজিমের উত্তর থেকে মন্তব্য (জোর যুক্ত করা হয়েছে)

যখন সম্ভব হয় তখন তা এড়াতে আমি ব্যক্তিগতভাবে কোনও সমাধান চেষ্টা ও সমাধানটি পছন্দ করি । এই ক্ষেত্রে, আমি মনে করি না যে লকগুলি ব্যবহার serializableকরা একটি ভারী হাতের পদ্ধতির, এবং আমি আত্মবিশ্বাসী থাকব এটি উচ্চতর সম্মতিটি ভালভাবে পরিচালনা করবে।

আমি যদি এই প্রথম বাক্যটিকে "এবং _ যখন বুদ্ধিমান" হিসাবে সংশোধন করা হয় তবে আমি তার সাথে একমত হব। প্রযুক্তিগতভাবে কোনও কিছু সম্ভব হওয়ার কারণে এর অর্থ এই নয় যে পরিস্থিতি (অর্থাত্ ব্যবহারের ক্ষেত্রে) এর দ্বারা উপকৃত হবে।

আমি এই পদ্ধতির সাথে যে বিষয়টি দেখছি তা হ'ল এটি প্রস্তাবিত হওয়ার চেয়ে বেশি লক করে। "সিরিয়ালাইজযোগ্য" -তে উদ্ধৃত ডকুমেন্টেশনগুলি পুনরায় পড়া গুরুত্বপূর্ণ, বিশেষত নিম্নলিখিতটি (জোর দেওয়া হয়েছে):

  • অন্যান্য লেনদেনগুলি মূল মান সহ নতুন সারি সন্নিবেশ করতে পারে না যা বর্তমান লেনদেন শেষ না হওয়া অবধি বর্তমান লেনদেনের যে কোনও বিবৃতি দ্বারা পঠিত কীগুলির পরিসরে পড়ে ।

এখন, উদাহরণ কোডটিতে মন্তব্যটি এখানে দেওয়া হয়েছে:

SELECT [Id]
FROM   dbo.NameLookup WITH (SERIALIZABLE) /* hold that key range for @vName */

অপারেটিভ শব্দটি "ব্যাপ্তি" রয়েছে। লক নেওয়া হচ্ছে এটি কেবলমাত্র মানের উপর নয় @vName, আরও সঠিকভাবে শুরু হওয়া একটি ব্যাপ্তিএই নতুন মানটি কোথায় যাবে সেই অবস্থানটি (যেমন নতুন মানটি যেখানে দু'দিকে বিদ্যমান মূল মানগুলির মধ্যে রয়েছে), তবে মানটি নিজেই নয়। অর্থ, বর্তমানে অনুসন্ধান করা মান (গুলি) এর উপর নির্ভর করে নতুন মানগুলি সন্নিবেশ করা থেকে অন্য প্রক্রিয়াগুলি অবরুদ্ধ করা হবে। যদি সীমাটির শীর্ষে অনুসন্ধান করা হচ্ছে, তবে সেই একই অবস্থানটি দখল করতে পারে এমন যে কোনও কিছু সন্নিবেশ করা অবরুদ্ধ করা হবে। উদাহরণস্বরূপ, যদি "a", "b" এবং "d" মানগুলি বিদ্যমান থাকে, তবে যদি একটি প্রক্রিয়া "f" তে নির্বাচন করে থাকে তবে মান "g" বা এমনকি "ই" সন্নিবেশ করা সম্ভব হবে না ( যেহেতু এর মধ্যে যে কোনও একটি "ডি" এর সাথে সাথেই আসবে)। তবে, "গ" এর মান সন্নিবেশ করা সম্ভব হবে কারণ এটি "সংরক্ষিত" ব্যাপ্তিতে স্থাপন করা হবে না।

নিম্নলিখিত আচরণে এই আচরণটি বর্ণনা করা উচিত:

(ক্যোরি ট্যাবে (অর্থাত্ সেশন) # 1)

INSERT INTO dbo.NameLookup ([ItemName]) VALUES (N'test5');

BEGIN TRAN;

SELECT [Id]
FROM   dbo.NameLookup WITH (SERIALIZABLE) /* hold that key range for @vName */
WHERE  ItemName = N'test8';

--ROLLBACK;

(ক্যোরি ট্যাবে (অর্থাত্ সেশন) # 2)

EXEC dbo.NameLookup_getset_byName @vName = N'test4';
-- works just fine

EXEC dbo.NameLookup_getset_byName @vName = N'test9';
-- hangs until you either hit "cancel" in this query tab,
-- OR issue a COMMIT or ROLLBACK in query tab #1

EXEC dbo.NameLookup_getset_byName @vName = N'test7';
-- hangs until you either hit "cancel" in this query tab,
-- OR issue a COMMIT or ROLLBACK in query tab #1

EXEC dbo.NameLookup_getset_byName @vName = N's';
-- works just fine

EXEC dbo.NameLookup_getset_byName @vName = N'u';
-- hangs until you either hit "cancel" in this query tab,
-- OR issue a COMMIT or ROLLBACK in query tab #1

একইভাবে, যদি মান "সি" বিদ্যমান থাকে এবং মান "এ" নির্বাচন করা হয় (এবং তাই লক করা হয়), তবে আপনি "ডি" এর মান সন্নিবেশ করতে পারেন, তবে "বি" এর মান নয়:

(ক্যোরি ট্যাবে (অর্থাত্ সেশন) # 1)

INSERT INTO dbo.NameLookup ([ItemName]) VALUES (N'testC');

BEGIN TRAN

SELECT [Id]
FROM   dbo.NameLookup WITH (SERIALIZABLE) /* hold that key range for @vName */
WHERE  ItemName = N'testA';

--ROLLBACK;

(ক্যোরি ট্যাবে (অর্থাত্ সেশন) # 2)

EXEC dbo.NameLookup_getset_byName @vName = N'testD';
-- works just fine

EXEC dbo.NameLookup_getset_byName @vName = N'testB';
-- hangs until you either hit "cancel" in this query tab,
-- OR issue a COMMIT or ROLLBACK in query tab #1

সত্যি কথা বলতে গেলে, আমার প্রস্তাবিত পদ্ধতির ক্ষেত্রে, যখন ব্যতিক্রম হয়, তখন লেনদেন লগের 4 টি প্রবেশ থাকবে যা এই "সিরিয়ালাইজযোগ্য লেনদেন" পদ্ধতির ক্ষেত্রে ঘটবে না। কিন্তু, আমি উপরে যেমন বলেছি, যদি ব্যতিক্রম সময়ের 1% (বা এমনকি 5%) ঘটে, তবে প্রাথমিকভাবে নির্বাচিত অস্থায়ীভাবে INSERT ক্রিয়াকলাপ অবরুদ্ধ করার সম্ভাব্য ক্ষেত্রে এর চেয়ে কম প্রভাব ফেলবে।

অপ্রাপ্তবয়স্ক হওয়া সত্ত্বেও, এই "সিরিয়ালাইজযোগ্য লেনদেন + OUTPUT ধারা" পদ্ধতির সাথে ইস্যুটি হ'ল OUTPUTধারাটি (এর বর্তমান ব্যবহারে) ফলাফল সেট হিসাবে ডেটা ফেরত পাঠায়। ফলাফলের সেটটিকে সাধারণ OUTPUTপ্যারামিটারের চেয়ে বেশি ওভারহেড (সম্ভবত উভয় পক্ষের: অভ্যন্তরীণ কার্সার পরিচালনা করার জন্য এসকিউএল সার্ভারে এবং অ্যাপ্লিকেশন স্তরে ডেটা রিডার অবজেক্টটি পরিচালনা করতে) প্রয়োজন হয় । প্রদত্ত যে আমরা কেবলমাত্র একটি একক স্কেলারের মান নিয়ে কাজ করছি এবং অনুমানটি মৃত্যুদণ্ড কার্যকর করার একটি উচ্চ ফ্রিকোয়েন্সি, ফলাফল সেটটির অতিরিক্ত ওভারহেড সম্ভবত যুক্ত করে।

এই OUTPUTধারাটি OUTPUTপ্যারামিটারটি ফেরত দেওয়ার মতোভাবে ব্যবহার করা যেতে পারে , এর জন্য অস্থায়ী টেবিল বা টেবিল ভেরিয়েবল তৈরি করতে অতিরিক্ত পদক্ষেপের প্রয়োজন হবে এবং তারপরে সেই টেম্প টেবিল / টেবিলের পরিবর্তনশীলটির মানটি OUTPUTপ্যারামিটারের মধ্যে বেছে নিতে হবে।

আরও স্পষ্টতা : সম্মতি এবং কার্য সম্পাদন সম্পর্কিত আমার বক্তব্যের @ এসকিএলজিমের প্রতিক্রিয়াতে (মূল উত্তরে) আমার প্রতিক্রিয়াতে @ এসকিএলজেমের জবাব (আপডেট উত্তর);

দুঃখিত, যদি এই অংশটি একটি সাম্প্রতিক বিট দীর্ঘ হয়, তবে এই মুহুর্তে আমরা দুটি পদ্ধতির সংক্ষেপে খুব নীচে আছি।

আমি বিশ্বাস করি যেভাবে তথ্য serializableউপস্থাপন করা হয়েছে আসল প্রশ্নে উপস্থাপিত দৃশ্যে ব্যবহার করার সময় যে পরিমাণ লকিংয়ের মুখোমুখি হতে পারে তার সম্পর্কে ভুল ধারণা অনুধাবন করতে পারে ।

হ্যাঁ, আমি স্বীকার করব যে আমি পক্ষপাতদুষ্ট, যদিও তা ন্যায্য:

  1. কোনও মানুষের পক্ষে পক্ষপাতদুষ্ট না হওয়া অসম্ভব, কমপক্ষে কিছুটা কম ডিগ্রীতে এবং আমি এটিকে সর্বনিম্ন রাখার চেষ্টা করি,
  2. প্রদত্ত উদাহরণটি সরল ছিল, তবে এটি অত্যধিক জটিলতা ছাড়াই আচরণটি প্রকাশের উদাহরণস্বরূপ ছিল। অতিরিক্ত ফ্রিকোয়েন্সি বোঝানোর উদ্দেশ্য ছিল না, যদিও আমি বুঝতে পারি যে আমি স্পষ্টভাবে অন্যথায় বর্ণনাও করি নি এবং এটি আসলে উপস্থিত থেকে বড় সমস্যা বোঝাতেও পড়তে পারে। আমি নীচে এটি স্পষ্ট করার চেষ্টা করব।
  3. আমি দুটি বিদ্যমান কী ("ক্যোয়ারী ট্যাব 1" এবং "অনুসন্ধান ট্যাব 2" ব্লকের দ্বিতীয় সেট) এর মধ্যে একটি সীমা লক করার একটি উদাহরণও অন্তর্ভুক্ত করেছি।
  4. আমি আমার পদ্ধতির "লুকানো ব্যয়" খুঁজে পেয়েছি (এবং স্বেচ্ছাসেবক), যে প্রতিবার INSERTঅনন্য বাধা লঙ্ঘনের কারণে চারটি অতিরিক্ত ট্রান লগ প্রবেশ করায় ব্যর্থ হয়। আমি অন্য উত্তর / পোস্টের কোনটিতে উল্লিখিত দেখিনি।

@ জিবিএন-এর "জেএফডিআই" পদ্ধতির বিষয়ে, মাইকেল জে স্বার্টের "উইলের জন্য কুৎসিত প্রগতিবাদ" পোস্ট, এবং মাইকের পোস্টের বিষয়ে অ্যারন বার্ট্র্যান্ডের মন্তব্য (তার পরীক্ষাগুলি কী দেখায় যে কর্মক্ষমতা হ্রাস পেয়েছে), এবং আপনার "মাইকেল জে অভিযোজিত সম্পর্কে মন্তব্য" স্টুয়ার্টের @ জিবিএন এর ট্রাই ক্যাচ জেএফডিআই পদ্ধতি "এর বর্ণনা অনুসারে অভিযোজিত:

আপনি যদি বিদ্যমান মানগুলি নির্বাচনের চেয়ে বেশি বার নতুন মান সন্নিবেশ করিয়ে থাকেন তবে এটি @ শ্রুতজকির সংস্করণের চেয়ে আরও বেশি পারফরম্যান্স হতে পারে। অন্যথায় আমি এই একের চেয়ে @ শ্রুতজকির সংস্করণটিকে পছন্দ করব।

"জেএফডিআই" পদ্ধতির সাথে সম্পর্কিত জিবিএন / মাইকেল / অ্যারন আলোচনার বিষয়ে, আমার পরামর্শকে জিবিএন-এর "জেএফডিআই" পদ্ধতির সাথে সমীকরণ করা ভুল হবে। "পান বা সন্নিবেশ" অপারেশন প্রকৃতির কারণে, কি একটি সুনির্দিষ্ট প্রয়োজন নেই SELECTপেতে IDবিদ্যমান রেকর্ডের জন্য মান। এই নির্বাচনটি IF EXISTSচেক হিসাবে কাজ করে , যা এই পদ্ধতিরটিকে হারুনের পরীক্ষাগুলির "চেকট্রিচ্যাচ" পরিবর্তনের সাথে আরও সমান করে তোলে। মাইকেল এর পুনরায় লিখিত কোড (এবং মাইকেল এর অভিযোজন আপনার চূড়ান্ত অভিযোজন) এছাড়াও WHERE NOT EXISTSপ্রথম একই চেক করতে একটি অন্তর্ভুক্ত । সুতরাং, আমার পরামর্শ (মাইকেল এর চূড়ান্ত কোড এবং তার চূড়ান্ত কোড আপনার অভিযোজন সহ) আসলে CATCHপ্রায়শই এই ব্লকে আঘাত করবে না । এটি কেবল তখনই হতে পারে যেখানে দুটি সেশন,ItemNameINSERT...SELECTঠিক একই মুহুর্তে যেমন উভয় অধিবেশন ঠিক একই মুহুর্তে একটি "সত্য" পায় WHERE NOT EXISTSএবং এইভাবে উভয়ই একই মুহূর্তে এটি করার চেষ্টা করে INSERT। অন্য কোনও প্রক্রিয়া যখন একই মুহূর্তে এটি করার চেষ্টা করছে না তখন কোনও বিদ্যমান নির্বাচন করা ItemNameবা একটি নতুন সন্নিবেশ করানোর চেয়ে খুব সুনির্দিষ্ট দৃশ্যটি প্রায়শই ঘটে ItemNameযায় ।

সমস্ত মনে সহ: কেন আমি আমার পদ্ধতির পছন্দ করি?

প্রথমে, "সিরিয়ালাইজযোগ্য" পদ্ধতির মধ্যে লকিংয়ের কী ঘটে তা দেখা যাক। উপরে উল্লিখিত হিসাবে, "রেঞ্জ" যেটি লক হয়ে যায় তা নতুন কী মানটি ফিট করতে পারে তার উভয় পাশের বিদ্যমান কী মানগুলির উপর নির্ভর করে। সীমাটির সূচনা বা শেষের সূচি যথাক্রমে সূচকের শুরু বা শেষ হতে পারে, যদি সেই দিকে কোনও বিদ্যমান মূল মান না থাকে। ধরুন আমাদের কাছে নিম্নলিখিত সূচক এবং কী রয়েছে ( ^সূচকের শুরুতে $এটি শেষের প্রতিনিধিত্ব করে):

Range #:    |--- 1 ---|--- 2 ---|--- 3 ---|--- 4 ---|
Key Value:  ^         C         F         J         $

সেশন 55 যদি এর মূল মান সন্নিবেশ করার চেষ্টা করে:

  • Aতারপর, # 1 (থেকে পরিসীমা ^থেকে C) লক করা আছে: অধিবেশন 56 মান ঢোকাতে পারেন না B, এমনকি যদি অনন্য এবং বৈধ (এখনও)। কিন্তু সেশন 56 মান ঢোকাতে পারবেন D, Gএবং M
  • Dতারপর, # 2 (থেকে পরিসীমা Cথেকে F) লক করা আছে: অধিবেশন 56 মান ঢোকাতে পারেন না E(এখনও)। কিন্তু সেশন 56 মান ঢোকাতে পারবেন A, Gএবং M
  • Mতারপর, # 4 (থেকে পরিসীমা Jথেকে $) লক করা আছে: অধিবেশন 56 মান ঢোকাতে পারেন না X(এখনও)। কিন্তু সেশন 56 মান ঢোকাতে পারবেন A, Dএবং G

আরও কী মূল্যবোধ যুক্ত হওয়ার সাথে সাথে মূল মানগুলির মধ্যে সীমাগুলি সংকীর্ণ হয়ে যায়, সুতরাং একই রেঞ্জের সাথে লড়াই করে একই সময়ে একাধিক মানের সন্নিবেশ হওয়ার সম্ভাবনা / ফ্রিকোয়েন্সি হ্রাস করে। স্বীকারযোগ্যভাবে, এটি কোনও বড় সমস্যা নয় এবং ভাগ্যক্রমে এটি এমন একটি সমস্যা হিসাবে দেখা যায় যা সময়ের সাথে প্রকৃতপক্ষে হ্রাস পায়।

আমার পদ্ধতির সমস্যাটি উপরে বর্ণিত ছিল: এটি কেবল তখন ঘটে যখন দুটি সেশন একই সময়ে একই কী মান সন্নিবেশ করানোর চেষ্টা করে । এই ক্ষেত্রে এটি নেমে আসে যার ঘটনার উচ্চতর সম্ভাবনা থাকে: দুটি ভিন্ন, তবুও নিকটবর্তী, মূল মান একই সাথে চেষ্টা করা হয়, বা একই মূল মান একই সাথে চেষ্টা করা হয়? আমি মনে করি উত্তরটি সন্নিবেশ করানোয় অ্যাপটির কাঠামোর মধ্যে রয়েছে তবে সাধারণভাবে বলতে গেলে আমি এটিকে আরও সম্ভবত এটি ধরে নেব যে দুটি ভিন্ন ভিন্ন মান যা কেবল একই পরিসীমা ভাগ করে নেওয়ার জন্য সন্নিবেশ করানো হচ্ছে। তবে সত্যিই জানার একমাত্র উপায় হ'ল উভয়ই ওপিএস সিস্টেমে পরীক্ষা করা।

এর পরে, দুটি পরিস্থিতি এবং প্রতিটি পদ্ধতির কীভাবে সেগুলি পরিচালনা করে তা বিবেচনা করা যাক:

  1. সমস্ত অনুরোধগুলি অনন্য কী মানগুলির জন্য রয়েছে:

    এই ক্ষেত্রে, CATCHআমার প্রস্তাবিত ব্লকটি কখনই প্রবেশ করা হয় না, সুতরাং কোনও "ইস্যু" হয় না (অর্থাত্ 4 টি ট্রান লগ এন্ট্রি এবং এটি করতে যে সময় লাগে)। তবে, "সিরিয়ালাইজযোগ্য" পদ্ধতির মধ্যে, এমনকি সমস্ত সন্নিবেশ অনন্য হয়ে থাকলেও, সর্বদা একই পরিসরে অন্যান্য সন্নিবেশগুলি অবরুদ্ধ করার কিছুটা সম্ভাবনা থাকবে (যদিও এটি খুব দীর্ঘ নয়)।

  2. একই মূল মানের জন্য অনুরোধের একই সময়ে উচ্চ-ফ্রিকোয়েন্সি:

    এই ক্ষেত্রে - অস্তিত্বহীন মূল মানগুলির জন্য আগত অনুরোধগুলির ক্ষেত্রে স্বাতন্ত্র্যের খুব কম ডিগ্রী - CATCHআমার পরামর্শের ব্লকটি নিয়মিত প্রবেশ করা হবে। এর প্রভাবটি হ'ল প্রতিটি ব্যর্থ সন্নিবেশকে অটো রোলব্যাক করতে হবে এবং লেনদেন লগটিতে 4 টি এন্ট্রি লিখতে হবে যা প্রতিবারের জন্য সামান্য পারফরম্যান্স হিট। তবে সামগ্রিক ক্রিয়াকলাপটি কখনই ব্যর্থ হয় না (কমপক্ষে এটির কারণে নয়)।

    ("আপডেটেড" পদ্ধতির পূর্ববর্তী সংস্করণে একটি সমস্যা ছিল যা এটিকে ডেডলকস থেকে ভোগার updlockঅনুমতি দেয় this এটি সমাধান করার জন্য একটি ইঙ্গিত যুক্ত করা হয়েছিল এবং এটি আর অচল অবস্থায় পড়ে না))কিন্তু, "সিরিয়ালাইজযোগ্য" পদ্ধতির (এমনকি আপডেট হওয়া, অপ্টিমাইজড সংস্করণে) অপারেশন অচল হয়ে যাবে। কেন? কারণ serializableআচরণটি কেবল INSERTপাঠ করা হয়েছে এবং তাই লক করা হয়েছে এমন সীমার ক্রিয়াকলাপকে বাধা দেয় ; এটি এই SELECTব্যাপ্তিতে অপারেশনগুলি আটকাবে না ।

    serializableপদ্ধতি, এই ক্ষেত্রে, কোন অতিরিক্ত ওভারহেড আছে বলে মনে হচ্ছে, এবং আমি যা পরামর্শ করছি চেয়ে সামান্য ভাল কার্য সম্পাদন করে হতে পারে।

পারফরম্যান্স সম্পর্কিত অনেক / সর্বাধিক আলোচনার সাথে সাথে, ফলাফলকে প্রভাবিত করতে পারে এমন অনেকগুলি কারণ থাকার কারণে, কিছু কীভাবে সঞ্চালিত হবে তা উপলব্ধি করার একমাত্র উপায় হ'ল লক্ষ্য পরিবেশে এটি চালানো হবে যেখানে চেষ্টা করা try এই মুহুর্তে এটি মতের বিষয় হবে না :)।


7

আপডেট উত্তর


@ শ্রুতজকির প্রতিক্রিয়া

অপ্রাপ্তবয়স্ক হওয়া সত্ত্বেও, এই "সিরিয়ালাইজযোগ্য লেনদেন + OUTPUT ধারা" পদ্ধতির সাথে ইস্যুটি হ'ল OUTPUT ধারা (বর্তমান ব্যবহারে) ফলাফল সেট হিসাবে ডেটা ফেরত পাঠায়। ফলাফলের সেটটিকে সাধারণ ওআউটিপিটি প্যারামিটারের চেয়ে বেশি ওভারহেড (সম্ভবত উভয় পক্ষের: অভ্যন্তরীণ কার্সার পরিচালনা করতে এসকিউএল সার্ভারে এবং অ্যাপ্লিকেশন স্তরে ডেটাআরডার বস্তু পরিচালনা করতে) প্রয়োজন হয়। প্রদত্ত যে আমরা কেবলমাত্র একটি একক স্কেলারের মান নিয়ে কাজ করছি এবং অনুমানটি মৃত্যুদণ্ড কার্যকর করার একটি উচ্চ ফ্রিকোয়েন্সি, ফলাফল সেটটির অতিরিক্ত ওভারহেড সম্ভবত যুক্ত করে।

আমি একমত, এবং যারা একই কারণে আমি ব্যবহারের আউটপুট পরামিতি না যখন বিচক্ষণ । আমার প্রাথমিক উত্তরটিতে আউটপুট প্যারামিটার ব্যবহার না করা আমার ভুল ছিল, আমি অলস হয়ে যাচ্ছিলাম।

এখানে একটি আউটপুট পরামিতি, অতিরিক্ত অপ্টিমাইজেশন, সহ ব্যবহার করে একটি সংশোধিত পদ্ধতি next value forযে @srutzky তার উত্তরে ব্যাখ্যা :

create procedure dbo.NameLookup_getset_byName (@vName nvarchar(50), @vValueId int output) as
begin
  set nocount on;
  set xact_abort on;
  set @vValueId = null;
  if nullif(@vName,'') is null                                 
    return;                                        /* if @vName is empty, return early */
  select  @vValueId = Id                                              /* go get the Id */
    from  dbo.NameLookup
    where ItemName = @vName;
  if @vValueId is not null                                 /* if we got the id, return */
    return;
  begin try;                                  /* if it is not there, then get the lock */
    begin tran;
      select  @vValueId = Id
        from  dbo.NameLookup with (updlock, serializable) /* hold key range for @vName */
        where ItemName = @vName;
      if @@rowcount = 0                    /* if we still do not have an Id for @vName */
      begin;                                         /* get a new Id and insert @vName */
        set @vValueId = next value for dbo.IdSequence;      /* get next sequence value */
        insert into dbo.NameLookup (ItemName, Id)
          values (@vName, @vValueId);
      end;
    commit tran;
  end try
  begin catch;
    if @@trancount > 0 
      begin;
        rollback transaction;
        throw;
      end;
  end catch;
end;

আপডেট নোট : updlockনির্বাচনের সাথে অন্তর্ভুক্ত করা এই দৃশ্যে যথাযথ লকগুলি ধরবে। @Srutzky জন্য ধন্যবাদ, যারা নির্দিষ্ট যে এই ডেডলক যখন শুধুমাত্র ব্যবহার হতে পারে serializableউপর select

দ্রষ্টব্য: এটি নাও হতে পারে তবে এটি সম্ভব হলে পদ্ধতিটি একটি মান সহ ডাকা হবে @vValueId, set @vValueId = null;পরে অন্তর্ভুক্ত করুন set xact_abort on;, অন্যথায় এটি অপসারণ করা যেতে পারে।


@ শ্রুতজকির কী-রেঞ্জ লকিং আচরণের উদাহরণগুলি সম্পর্কিত:

@ শ্রুতজকি কেবল তার টেবিলে একটি মান ব্যবহার করে এবং কী টেস্টের জন্য কী পরীক্ষার জন্য "পরের" / "ইনফিনিটি" কীটিকে লক করে key তার পরীক্ষাগুলি সেই পরিস্থিতিতে কী ঘটেছিল তা বোঝানোর জন্য, আমি বিশ্বাস করি যেভাবে তথ্য serializableউপস্থাপন করা হয়েছে তা মূল প্রশ্নে উপস্থাপিত পরিস্থিতিতে দৃশ্যে ব্যবহার করার সময় যে পরিমাণ লকিংয়ের মুখোমুখি হতে পারে তা সম্পর্কে ভুল ধারণা অনুভব করতে পারে ।

যদিও তিনি তার ব্যাখ্যা এবং কী রেঞ্জ লক করার উদাহরণগুলি উপস্থাপন করেছেন সেভাবে একটি পক্ষপাত (সম্ভবত মিথ্যাভাবে) বুঝতে পেরেছি, সেগুলি এখনও সঠিক।


আরও গবেষণার পরে, আমি মাইকেল জে স্বার্ট: মিথথাস্টিং: সমকালীন আপডেট / সন্নিবেশ সমাধানগুলি দ্বারা 2011 সালে একটি বিশেষ প্রাসঙ্গিক ব্লগ নিবন্ধটি পেয়েছি । এতে, তিনি নির্ভুলতা এবং একযোগের জন্য একাধিক পদ্ধতি পরীক্ষা করেন। পদ্ধতি 4: বর্ধিত আইসোলেশন + ফাইন টিউনিং লকগুলি স্যাম জাফরনের পোস্ট সন্নিবেশ করা বা এসকিউএল সার্ভারের জন্য আপডেট প্যাটার্ন এবং তার প্রত্যাশা পূরণের মূল পরীক্ষার একমাত্র পদ্ধতি (পরবর্তী সময়ে যোগ দিয়েছিল merge with (holdlock)) এর উপর ভিত্তি করে ।

২০১ of সালের ফেব্রুয়ারিতে মাইকেল জে স্বার্ট দ্য উইনের জন্য অগলি প্র্যাকম্যাটিজম পোস্ট করেছিলেন । সেই পোস্টে, তিনি লকিং কমাতে (তার উপরের পদ্ধতিতে আমি অন্তর্ভুক্ত করেছি) তার জাফরান উপস্থাপন পদ্ধতিতে তৈরি করা কিছু অতিরিক্ত টিউনিং কভার করে।

এই পরিবর্তনগুলি করার পরে, মাইকেল খুশি হন নি যে তার পদ্ধতিটি আরও জটিল দেখা শুরু হয়েছিল এবং ক্রিস নামে একটি কোলিজের সাথে পরামর্শ করল। ক্রিস মূল সমস্ত Mythbusters পোস্ট পড়ে এবং সমস্ত মন্তব্য পড়ে এবং @ gbn এর ট্রাই ক্যাচ জেএফডিআই প্যাটার্ন সম্পর্কে জিজ্ঞাসা করেছিল । এই প্যাটার্নটি @ শ্রুতজকির উত্তরের অনুরূপ, এবং মাইকেল সেই উদাহরণটি ব্যবহার করে শেষ করেছেন।

মাইকেল জে স্বার্ট:

গতকাল আমি একযোগে করার সর্বোত্তম উপায় সম্পর্কে আমার মন পরিবর্তন করেছি। আমি মাইথবাস্টিংয়ে বেশ কয়েকটি পদ্ধতি বর্ণনা করি: সমকালীন আপডেট / সন্নিবেশ সমাধানগুলি। আমার পছন্দের পদ্ধতিটি হল বিচ্ছিন্নতা স্তর এবং সূক্ষ্ম সুরের লকগুলি বাড়ানো।

অন্তত আমার পছন্দ ছিল। আমি সম্প্রতি জিবিএন মন্তব্যে প্রস্তাবিত একটি পদ্ধতি ব্যবহার করার জন্য আমার দৃষ্টিভঙ্গি পরিবর্তন করেছি। তিনি তার পদ্ধতিটিকে "ট্রাই ক্যাচ জেএফডিআই প্যাটার্ন" হিসাবে বর্ণনা করেছেন। সাধারণত আমি এর মতো সমাধান এড়িয়ে চলি। থাম্বের একটি নিয়ম রয়েছে যা বলে যে বিকাশকারীদের নিয়ন্ত্রণ প্রবাহের জন্য ত্রুটি বা ব্যতিক্রমগুলি ধরায় নির্ভর করা উচিত নয়। তবে আমি গতকালই সেই নিয়মটি ভঙ্গ করেছি।

যাইহোক, আমি "জেএফডিআই" প্যাটার্নটির জন্য জিবিএন-এর বর্ণনা পছন্দ করি। এটি আমাকে শিয়া লেবেউফের প্রেরণাদায়ী ভিডিওর কথা মনে করিয়ে দেয়।


আমার মতে, উভয় সমাধানই কার্যকর are যদিও আমি এখনও বিচ্ছিন্নতা স্তর এবং সূক্ষ্ম সুরের লকগুলি বাড়াতে পছন্দ করি, @ শ্রুতজকির উত্তরটিও বৈধ এবং আপনার নির্দিষ্ট পরিস্থিতিতে আরও পারফরম্যান্ট হতে পারে বা নাও পারে।

সম্ভবত ভবিষ্যতে আমিও একই সিদ্ধান্তে পৌঁছে যাব যে মাইকেল জে স্বার্ট করেছিলেন, তবে আমি এখনও সেখানে নেই।


এটি আমার পছন্দ নয়, তবে মাইকেল জে। স্টিয়ার্টের @ জিবিএন এর ট্রাই ক্যাচ জেএফডিআই পদ্ধতিটি খাপ খাইয়ে নেওয়ার ক্ষেত্রে আমার গ্রহণযোগ্যতাটি এখানে রয়েছে :

create procedure dbo.NameLookup_JFDI (
    @vName nvarchar(50)
  , @vValueId int output
  ) as
begin
  set nocount on;
  set xact_abort on;
  set @vValueId = null;
  if nullif(@vName,'') is null                                 
    return;                     /* if @vName is empty, return early */
  begin try                                                 /* JFDI */
    insert into dbo.NameLookup (ItemName)
      select @vName
      where not exists (
        select 1
          from dbo.NameLookup
          where ItemName = @vName);
  end try
  begin catch        /* ignore duplicate key errors, throw the rest */
    if error_number() not in (2601, 2627) throw;
  end catch
  select  @vValueId = Id                              /* get the Id */
    from  dbo.NameLookup
    where ItemName = @vName
  end;

আপনি যদি বিদ্যমান মানগুলি নির্বাচনের চেয়ে বেশি বার নতুন মান সন্নিবেশ করিয়ে থাকেন তবে এটি @ শ্রুতজকির সংস্করণের চেয়ে আরও বেশি পারফরম্যান্স হতে পারে । অন্যথায় আমি এই একের চেয়ে @ শ্রুতজকির সংস্করণটিকে পছন্দ করব ।

মাইকেল জে স্বার্টের পোস্টে অ্যারন বার্ট্র্যান্ডের মন্তব্যগুলি প্রাসঙ্গিক পরীক্ষার সাথে লিঙ্ক করেছে এবং এই বিনিময়টির দিকে পরিচালিত করেছে। মন্তব্য অধ্যায় থেকে উদ্ধৃতাংশ জয়ের কুশ্রী প্রয়োগবাদ :

কখনও কখনও, যদিও, জেএফডিআই সামগ্রিকভাবে আরও খারাপ পারফরম্যান্সের দিকে পরিচালিত করে, কলগুলির%% ব্যর্থতার উপর নির্ভর করে। ব্যতিক্রম উত্থাপনের যথেষ্ট ওভারহেড রয়েছে। আমি কয়েকটা পোস্টে এটি দেখিয়েছি:

http://sqlperformance.com/2012/08/t-sql-queries/error-handling

https://www.mssqltips.com/sqlservertip/2632/checking-for-potential-constraint-violations-before-entering-sql-server-try-and-catch-logic/

অ্যারন বারট্রান্ডের মন্তব্য - ১১ ফেব্রুয়ারী, ২০১ @ @ ১১:৪৯ এএম

এবং এর জবাব:

আপনি ঠিক অ্যারন, এবং আমরা এটি পরীক্ষা করেছি।

দেখা যাচ্ছে যে আমাদের ক্ষেত্রে, কলগুলি ব্যর্থ হয়েছে যেগুলি শতাংশ ব্যর্থ হয়েছিল 0 (যখন কাছের শতাংশে গোল হয়)।

আমি মনে করি আপনি এই বিন্দুটি ব্যাখ্যা করেছেন যে যতটা সম্ভব, বিধি-বিধানের নিয়মগুলি দ্বারা কেস বাই কেস ভিত্তিতে জিনিসগুলি মূল্যায়ন করুন।

এটিও কেন আমরা কঠোরভাবে প্রয়োজনীয় নয় যেখানে ক্লজটি বিদ্যমান নেই added

মাইকেল জে স্বার্ট দ্বারা মন্তব্য - ফেব্রুয়ারী 11, 2016 @ 11:57 এএম


নতুন লিঙ্ক:


আসল উত্তর


আমি এখনও স্যাম জাফরান উপস্থাপনা বনাম বনাম ব্যবহার পছন্দ করি merge, বিশেষত যখন একটি একক সারি নিয়ে কাজ করে।

আমি এই উপস্থাপন পদ্ধতিটিকে এই পরিস্থিতিতে এইভাবে মানিয়ে নেব:

declare @vName nvarchar(50) = 'Invader';
declare @vValueId int       = null;

if nullif(@vName,'') is not null /* this gets your where condition taken care of before we start doing anything */
begin tran;
  select @vValueId = Id
    from dbo.NameLookup with (serializable) 
    where ItemName = @vName;
  if @@rowcount > 0 
    begin;
      select @vValueId as id;
    end;
    else
    begin;
      insert into dbo.NameLookup (ItemName)
        output inserted.id
          values (@vName);
      end;
commit tran;

আমি আপনার নামকরণের সাথে সামঞ্জস্য বজায় রাখব এবং serializableএকইরূপে holdlockএকটি বেছে নিন এবং এর ব্যবহারে ধারাবাহিক থাকব। আমি ব্যবহার করার প্রবণতা serializableকারণ এটি উল্লেখ করার সময় একই নাম ব্যবহার করা হয় set transaction isolation level serializable

ব্যবহারের serializableবা holdlockএকটি সীমার লক মান উপর ভিত্তি করে নেওয়া হয় @vNameতোলে যা অন্য কোন অপারেশন অপেক্ষা করুন যদি তারা নির্বাচন বা মান ঢোকাতে মধ্যে dbo.NameLookupযে মান অন্তর্ভুক্ত whereদফা।

ব্যাপ্তি লকটি সঠিকভাবে কাজ করার জন্য, ItemNameকলামটিতে একটি সূচক থাকা প্রয়োজন এটি ব্যবহার করার সময় এটি প্রয়োগ হয় merge


এখানে কি পদ্ধতি মত চেহারা হবে বেশিরভাগই নিম্নলিখিত ত্রুটি পরিচালনা জন্য Erland Sommarskog এর whitepapers ব্যবহার throw। আপনি throwযদি নিজের ত্রুটিগুলি কীভাবে উত্থাপন করছেন তা যদি না হয় তবে আপনার বাকি পদ্ধতির সাথে সামঞ্জস্য রাখতে এটি পরিবর্তন করুন:

create procedure dbo.NameLookup_getset_byName (@vName nvarchar(50) ) as
begin
  set nocount on;
  set xact_abort on;
  declare @vValueId int;
  if nullif(@vName,'') is null /* if @vName is null or empty, select Id as null */
    begin
      select Id = cast(null as int);
    end 
    else                       /* else go get the Id */
    begin try;
      begin tran;
        select @vValueId = Id
          from dbo.NameLookup with (serializable) /* hold key range for @vName */
          where ItemName = @vName;
        if @@rowcount > 0      /* if we have an Id for @vName select @vValueId */
          begin;
            select @vValueId as Id; 
          end;
          else                     /* else insert @vName and output the new Id */
          begin;
            insert into dbo.NameLookup (ItemName)
              output inserted.Id
                values (@vName);
            end;
      commit tran;
    end try
    begin catch;
      if @@trancount > 0 
        begin;
          rollback transaction;
          throw;
        end;
    end catch;
  end;
go

উপরোক্ত পদ্ধতিতে কী চলছে তা সংক্ষিপ্ত করার জন্য: set nocount on; set xact_abort on;যেমন আপনি সর্বদা করেন তবে ফলস্বরূপ আমাদের ইনপুট পরিবর্তনশীল is nullবা খালি থাকলে select id = cast(null as int)। যদি এটি নালাগুলি বা খালি না হয়, তবে সেই স্থানটি যদি না থাকে Idতবে আমাদের স্থান পরিবর্তন করতে পারেন। যদি সেখানে থাকে তবে এটি পাঠিয়ে দিন। যদি এটি না থাকে তবে এটি inোকান এবং নতুনটি প্রেরণ করুন ।IdId

এদিকে, একই মানের জন্য আইডি সন্ধান করার জন্য এই পদ্ধতির অন্যান্য কলগুলি প্রথম লেনদেন শেষ না হওয়া পর্যন্ত অপেক্ষা করবে এবং তারপরে এটি নির্বাচন করুন এবং ফেরত দিন। এই পদ্ধতির অন্যান্য কল বা অন্যান্য মানগুলির সন্ধানকারী অন্যান্য বিবৃতিগুলি অবিরত থাকবে কারণ এটি একটি পথে নেই।

আমি @ শ্রুতজকির সাথে একমত যে আপনি এই ধরণের সমস্যার জন্য সংঘর্ষগুলি পরিচালনা করতে পারেন এবং ব্যতিক্রমগুলি গিলে ফেলতে পারেন, আমি ব্যক্তিগতভাবে তা সম্ভব না হলে এড়াতে কোনও সমাধানের চেষ্টা ও তালি দেওয়া পছন্দ করি। এই ক্ষেত্রে, আমি মনে করি না যে লকগুলি ব্যবহার serializableকরা একটি ভারী হাতের পদ্ধতির, এবং আমি আত্মবিশ্বাসী থাকব এটি উচ্চতর সম্মতিটি ভালভাবে পরিচালনা করবে।

টেবিলের ইঙ্গিতগুলিতে SQL সার্ভার ডকুমেন্টেশনserializableholdlock থেকে উদ্ধৃতি / :

SERIALIZABLE

হোল্ডলকের সমতুল্য। লেনদেন সম্পন্ন হয়েছে কি না, প্রয়োজনীয় টেবিল বা ডেটা পৃষ্ঠার আর প্রয়োজন নেই, ভাগ করে নেওয়া লক ছাড়ার পরিবর্তে লেনদেন সম্পন্ন না হওয়া পর্যন্ত শেয়ার লকগুলিকে আরও বাধাবদ্ধ করে তোলে। সেরিয়ালিজাবল বিচ্ছিন্নতা স্তরে চলমান লেনদেনের মতো একই শব্দার্থক দিয়ে স্ক্যানটি সঞ্চালিত হয়। বিচ্ছিন্নতা স্তরগুলি সম্পর্কে আরও তথ্যের জন্য, সারণী বিচ্ছিন্নকরণ স্তরটি (লেনদেন-এসকিউএল) দেখুন।

লেনদেনের বিচ্ছিন্নতা স্তরে স্কয়ার সার্ভার ডকুমেন্টেশন থেকে উদ্ধৃতিserializable

সিরিয়ালযোগ্য নিম্নলিখিতটি নির্দিষ্ট করে:

  • বিবৃতিগুলি এমন ডেটা পড়তে পারে না যা পরিবর্তিত হয়েছে তবে অন্যান্য লেনদেনের দ্বারা প্রতিশ্রুতিবদ্ধ হয়নি।

  • বর্তমান লেনদেন শেষ না হওয়া অবধি বর্তমান কোনও লেনদেন দ্বারা পড়া পড়া ডেটা অন্য কোনও লেনদেন সংশোধন করতে পারে না।

  • অন্যান্য লেনদেনগুলি মূল মান সহ নতুন সারি সন্নিবেশ করতে পারে না যা বর্তমান লেনদেন শেষ না হওয়া অবধি বর্তমান লেনদেনের যে কোনও বিবৃতি দ্বারা পঠিত কীগুলির পরিসরে পড়ে।


উপরের সমাধান সম্পর্কিত লিঙ্কগুলি:

MERGEএকটি দৃষ্টিনন্দন ইতিহাস রয়েছে এবং কোডটি আপনি যে সমস্ত সিনট্যাক্সের আওতায় এটি দেখতে চান সেটি আচরণ করছে কিনা তা নিশ্চিত করার জন্য এটি আরও বেশি ঝুঁকির মধ্যে পড়ে বলে মনে হচ্ছে। প্রাসঙ্গিক mergeনিবন্ধ:

একটা শেষ লিঙ্ক, কেন্দ্রের লিটল একটি করেনি রুক্ষ তুলনা mergeবনামinsert with left join , সতর্কীকরণ যেখানে সে বলে "আমি এই পুঙ্খানুপুঙ্খ লোড টেস্টিং করিনি" সঙ্গে, কিন্তু এটি এখনও একটি ভাল পঠিত।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.