এক্সএমএল প্যারামিটার ব্যবহার করে একাধিক ডেটা সংরক্ষণ করার সময় মার্জ কোয়েরি ব্যবহার কীভাবে এড়ানো যায়?


10

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

এখন, আমি এটি একটি XML প্যারামিটার লাগে এমন একটি সঞ্চিত পদ্ধতিতে এটি অর্জনের চেষ্টা করছি am আমি এক্সএমএল ব্যবহার করছি এবং টেবিল-মূল্যবান প্যারাম নয় কারণ কারণ, পরে এসকিউএল এ আমাকে কাস্টম টাইপ তৈরি করতে হবে এবং এই প্রকারটি সঞ্চিত পদ্ধতির সাথে যুক্ত করতে হবে। আমি যদি কখনও আমার সঞ্চিত পদ্ধতিতে বা আমার ডিবি স্কিমার রাস্তায় কিছু পরিবর্তন করি তবে আমাকে সঞ্চিত পদ্ধতি এবং কাস্টম প্রকার উভয়ই আবার করতে হবে। আমি এই পরিস্থিতি এড়াতে চাই তদুপরি, এক্সপিএমের চেয়ে টিভিপির যে শ্রেষ্ঠত্ব রয়েছে তা আমার পরিস্থিতির জন্য কার্যকর নয় কারণ, আমার ডেটা অ্যারের আকার কখনই 1000 এর বেশি হবে না This এর অর্থ এখানে প্রস্তাবিত সমাধানটি আমি ব্যবহার করতে পারি না: এসকিউএল সার্ভার ২০০৮-এ এক্সএমএল ব্যবহার করে একাধিক রেকর্ড কীভাবে সন্নিবেশ করা যায়?

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

আমি আশা করছিলাম যে এক্সএমএল থেকে মানগুলি সুনির্দিষ্ট করতে আমি কেবল নীচের প্রশ্নেরগুলি ব্যবহার করব। কিন্তু এটি কাজ করে না। যখন ইনপুটটি একক সারিতে থাকে তখন এই পদ্ধতির কাজ করার কথা।

begin tran
   update table with (serializable) set select * from xml_param
   where key = @key

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

পরবর্তী বিকল্প হ'ল উপস্থিত বা নিম্নরূপের এর কোনও একটির প্রকরণের উপস্থিতি সম্পূর্ণরূপে ব্যবহার করা। তবে, আমি এটিকে উপ-অনুকূল দক্ষতার ভিত্তিতে প্রত্যাখ্যান করছি:

IF (SELECT COUNT ... ) > 0
    UPDATE
ELSE
    INSERT

পরের বিকল্পটি এখানে বর্ণিত হিসাবে মার্জ বিবৃতিটি ব্যবহার করছে: http ://www.datediaj Journal.com/features/mssql/ using-the- खेळाडू-statement-to-perform-an-upsert.html । তবে, তারপরে আমি এখানে মার্জ কোয়েরি নিয়ে সমস্যাগুলি সম্পর্কে পড়লাম: http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers- নিম- স্টেটমেন্ট / । এই কারণে, আমি মার্জটিকে এড়াতে চেষ্টা করছি।

সুতরাং, এখন আমার প্রশ্নটি হল: এসকিউএল সার্ভার ২০০ টি সঞ্চিত পদ্ধতিতে এক্সএমএল প্যারামিটার ব্যবহার করে একাধিক উপকরণ অর্জনের জন্য অন্য কোনও বিকল্প বা আরও ভাল উপায় আছে কি?

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


ভবিষ্যতে প্রোকে পরিবর্তনগুলি এড়াতে চেষ্টা করা কোনও টিভিপি ব্যবহার না করা ভাল কারণ নয়। যদি ডেটা পরিবর্তনে পাস হয়ে যায় তবে আপনি কোডে কোনওভাবেই পরিবর্তন আনতে পারবেন।
ম্যাক্স ভার্নন

1
@ ম্যাক্স ভার্নন আমার প্রথমে একই ধারণা ছিল এবং প্রায় একটি খুব অনুরূপ মন্তব্য করেছি কারণ এটি কেবল টিভিপি এড়ানোর কারণ নয়। তবে তারা কিছুটা বেশি চেষ্টা করে এবং "1000 সারি বেশি নয়" এর সতর্কতার সাথে (কখনও কখনও বোঝা যায়, বা এমনকি এমনকি প্রায়শই বোঝা যায়?) এটি কিছুটা টস-আপও। তবে, আমি মনে করি আমার উত্তরটি বলার যোগ্যতা অর্জন করতে হবে যে এক সাথে <1000 সারি XML থেকে খুব বেশি আলাদা নয় যতক্ষণ না এটি পর পর 10 কে বার বলা হয় না। তারপরে ছোটখাটো পারফরম্যান্সের পার্থক্য অবশ্যই বাড়িয়ে তোলে।
সলোমন রুটজকি

MERGEবার্ট্র্যান্ডের সাথে যে বিষয়গুলি উল্লেখ করা হয়েছে সেগুলি বেশিরভাগ প্রান্তের মামলা এবং অদক্ষতা, স্টপারকে দেখায় না - এমএস এটি বাস্তব মাইনফিল্ড না হলে মুক্তি দিত না। আপনি কি নিশ্চিত যে আপনি যে কনভলিউশনগুলি এড়াতে যাচ্ছেন MERGEসেগুলি সংরক্ষণের চেয়ে বেশি সম্ভাব্য ত্রুটি তৈরি করছে না?
সমস্ত ব্যবসায়ের

@ জোনফএলট্রেডস ন্যায্য বলতে, আমি যা প্রস্তাব করেছি তা আসলে তুলনার তুলনায় বিশৃঙ্খল নয় MERGE। MERGE এর INSERT এবং আপডেটের পদক্ষেপগুলি এখনও আলাদাভাবে প্রক্রিয়াজাত করা হয়। আমার পদ্ধতির মূল পার্থক্যটি হ'ল টেবিলের পরিবর্তনশীল যা আপডেট হওয়া রেকর্ড আইডি এবং ডিলিট ক্যোয়ারি ধারণ করে যা সেই টেবিল ভেরিয়েবলটি ইনকামিং ডেটার টেম্প টেবিল থেকে সেই রেকর্ডগুলি সরাতে ব্যবহার করে। এবং আমি মনে করি যে টেম্প টেবিলের উপরে ফেলে দেওয়ার পরিবর্তে সোর্সটি @ এক্সএমএলপারাম.নোড () থেকে সরাসরি হতে পারে, তবে তবুও, এই প্রান্তের কোনও ক্ষেত্রে নিজেকে খুঁজে পাওয়ার বিষয়ে চিন্তা করার মতো অতিরিক্ত জিনিস নয়; - )।
সলোমন রুটজকি

উত্তর:


12

উত্সটি এক্সএমএল হোক বা একটি টিভিপি বিশাল পার্থক্য রাখে না। সামগ্রিক অপারেশন মূলত:

  1. বিদ্যমান সারি আপডেট করুন
  2. সারি সারি অন্তর্ভুক্ত করুন

আপনি এই ক্রমে এটি করুন কারণ আপনি যদি প্রথমে অন্তর্ভুক্ত করেন তবে আপডেটটি পাওয়ার জন্য সমস্ত সারি বিদ্যমান এবং আপনি সন্নিবেশ করা কোনও সারিগুলির জন্য বারবার কাজ করবেন।

এর বাইরে এটি সম্পাদন করার বিভিন্ন উপায় এবং এর বাইরে কিছু অতিরিক্ত দক্ষতা তুলে ধরার বিভিন্ন উপায় রয়েছে।

খালি ন্যূনতম দিয়ে শুরু করা যাক। যেহেতু এক্সএমএল উত্তোলন করা সম্ভবত এই অপারেশনের অন্যতম ব্যয়বহুল অংশ হতে পারে (যদি সবচেয়ে ব্যয়বহুল না হয়), তাই আমরা দুবার এটি করতে চাই না (যেমন আমাদের দুটি কার্য সম্পাদন করতে হবে)। সুতরাং, আমরা একটি টেম্প টেবিল তৈরি করি এবং এতে এক্সএমএল থেকে ডেটা বের করি:

CREATE TABLE #TempImport
(
  Field1 DataType1,
  Field2 DataType2,
  ...
);

INSERT INTO #TempImport (Field1, Field2, ...)
  SELECT tab.col.value('XQueryForField1', 'DataType') AS [Field1],
         tab.col.value('XQueryForField2', 'DataType') AS [Field2],
         ...
  FROM   @XmlInputParam.nodes('XQuery') tab(col);

সেখান থেকে আমরা আপডেট এবং তারপরে ইনসার্ট করি:

UPDATE tab
SET    tab.Field1 = tmp.Field1,
       tab.Field2 = tmp.Field2,
       ...
FROM   [SchemaName].[TableName] tab
INNER JOIN #TempImport tmp
        ON tmp.IDField = tab.IDField
        ... -- more fields if PK or alternate key is composite

INSERT INTO [SchemaName].[TableName]
  (Field1, Field2, ...)
  SELECT tmp.Field1, tmp.Field2, ...
  FROM   #TempImport tmp
  WHERE  NOT EXISTS (
                       SELECT  *
                       FROM    [SchemaName].[TableName] tab
                       WHERE   tab.IDField = tmp.IDField
                       ... -- more fields if PK or alternate key is composite
                     );

এখন আমাদের বেসিক অপারেশনটি বন্ধ হয়ে গেছে, আমরা অনুকূলিত করার জন্য কয়েকটি জিনিস করতে পারি:

  1. টেম্প টেবিলের মধ্যে Rোকানো @@ ROWCOUNT টি ক্যাপচার করুন এবং আপডেটের @@ ROWCOUNT এর সাথে তুলনা করুন। যদি সেগুলি একই হয় তবে আমরা INSERT এড়িয়ে যেতে পারি

  2. OUTPUT ধারাটির মাধ্যমে আপডেট হওয়া আইডি মানগুলি ক্যাপচার করুন এবং টেম্প টেবিল থেকে এগুলি মুছুন। তারপরে INSERT এর দরকার নেইWHERE NOT EXISTS(...)

  3. যদি আগত তথ্যগুলিতে কোনও সারি থাকে তবে তা করা উচিত সিঙ্ক করা নয় (যেমন োকানো বা আপডেট করা যায় না), তবে সেই রেকর্ডগুলি আপডেট করার আগে মুছে ফেলা উচিত

CREATE TABLE #TempImport
(
  Field1 DataType1,
  Field2 DataType2,
  ...
);

DECLARE @ImportRows INT;
DECLARE @UpdatedIDs TABLE ([IDField] INT NOT NULL);

BEGIN TRY

  INSERT INTO #TempImport (Field1, Field2, ...)
    SELECT tab.col.value('XQueryForField1', 'DataType') AS [Field1],
           tab.col.value('XQueryForField2', 'DataType') AS [Field2],
           ...
    FROM   @XmlInputParam.nodes('XQuery') tab(col);

  SET @ImportRows = @@ROWCOUNT;

  IF (@ImportRows = 0)
  BEGIN
    RAISERROR('Seriously?', 16, 1); -- no rows to import
  END;

  -- optional: test to see if it helps or hurts
  -- ALTER TABLE #TempImport
  --   ADD CONSTRAINT [PK_#TempImport]
  --   PRIMARY KEY CLUSTERED (PKField ASC)
  --   WITH FILLFACTOR = 100;


  -- optional: remove any records that should not be synced
  DELETE tmp
  FROM   #TempImport tmp
  INNER JOIN [SchemaName].[TableName] tab
          ON tab.IDField = tmp.IDField
          ... -- more fields if PK or alternate key is composite
  WHERE  tmp.ModifiedDate < tab.ModifiedDate;

  BEGIN TRAN;

  UPDATE tab
  SET    tab.Field1 = tmp.Field1,
         tab.Field2 = tmp.Field2,
         ...
  OUTPUT INSERTED.IDField
  INTO   @UpdatedIDs ([IDField]) -- capture IDs that are updated
  FROM   [SchemaName].[TableName] tab
  INNER JOIN #TempImport tmp
          ON tmp.IDField = tab.IDField
          ... -- more fields if PK or alternate key is composite

  IF (@@ROWCOUNT < @ImportRows) -- if all rows were updates then skip, else insert remaining
  BEGIN
    -- get rid of rows that were updates, leaving only the ones to insert
    DELETE tmp
    FROM   #TempImport tmp
    INNER JOIN @UpdatedIDs del
            ON del.[IDField] = tmp.[IDField];

    -- OR, rather than the DELETE, maybe add a column to #TempImport for:
    -- [IsUpdate] BIT NOT NULL DEFAULT (0)
    -- Then UPDATE #TempImport SET [IsUpdate] = 1 JOIN @UpdatedIDs ON [IDField]
    -- Then, in below INSERT, add:  WHERE [IsUpdate] = 0

    INSERT INTO [SchemaName].[TableName]
      (Field1, Field2, ...)
      SELECT tmp.Field1, tmp.Field2, ...
      FROM   #TempImport tmp
  END;

  COMMIT TRAN;

END TRY
BEGIN CATCH
  IF (@@TRANCOUNT > 0)
  BEGIN
    ROLLBACK;
  END;

  -- THROW; -- if using SQL 2012 or newer, use this and remove the following 3 lines
  DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE();
  RAISERROR(@ErrorMessage, 16, 1);
  RETURN;
END CATCH;

আমি এই মডেলটি বেশ কয়েকবার আমদানি / ইটিএলগুলিতে ব্যবহার করেছি যার মধ্যে রয়েছে প্রায় ২০ কেটের মোট সেটের মধ্যে একটি ব্যাচের মধ্যে 1000 টিরও বেশি সারি রয়েছে বা সম্ভবত 500 টি রয়েছে - এক মিলিয়ন সারি। তবে, টেম্প টেবিলের বাইরে আপডেট হওয়া সারিগুলি মুছে ফেলার মধ্যে পারফরম্যান্সের পার্থক্যটি কেবলমাত্র [ইসআপডেট] ফিল্ডটি আপডেট করে পরীক্ষা করেছি না।


একবারে আমদানি করার জন্য সর্বাধিক, 1000 টি সারির কারণে টিভিপিতে এক্সএমএল ব্যবহারের সিদ্ধান্ত সম্পর্কিত নোটটি (প্রশ্নটিতে উল্লিখিত):

এটি যদি এখানে এবং সেখানে কয়েকবার ডাকা হয়, তবে সম্ভবত টিভিভিতে সামান্য পারফরম্যান্স লাভ অতিরিক্ত রক্ষণাবেক্ষণ ব্যয়ের জন্য মূল্যহীন নাও হতে পারে (ব্যবহারকারী-সংজ্ঞায়িত সারণীর প্রকার, অ্যাপ্লিকেশন কোড পরিবর্তন ইত্যাদির আগে প্র্যাকটি বাদ দিতে হবে) । তবে আপনি যদি 4 মিলিয়ন সারি আমদানি করে থাকেন, একবারে 1000 প্রেরণ করছেন, এটি 4000 মৃত্যুদণ্ড কার্যকর হবে (এবং এটি কীভাবে ভেঙে ফেলা যায় তা পার্স করার জন্য 4 মিলিয়ন সারি এক্সএমএল), এবং এমনকি যখন কয়েকবার মৃত্যুদন্ড কার্যকর করা হবে তখনও সামান্য পারফরম্যান্সের পার্থক্য একটি লক্ষণীয় পার্থক্য যোগ করুন।

বলা হচ্ছে যে, আমি যে পদ্ধতিটি বর্ণনা করেছি সেভাবে নির্বাচনগুলি @ টিএমপি থেকে SEML FXM @XMLInputParam এর পরিবর্তে পরিবর্তিত হবে না। যেহেতু টিভিপি কেবল পঠনযোগ্য তাই আপনি সেগুলি থেকে মুছতে সক্ষম হবেন না। আমার ধারণা আপনি WHERE NOT EXISTS(SELECT * FROM @UpdateIDs ids WHERE ids.IDField = tmp.IDField)সাধারণের পরিবর্তে কেবল সেই চূড়ান্ত নির্বাচনকে (INSERT এর সাথে আবদ্ধ) যুক্ত করতে পারেন WHERE IsUpdate = 0। আপনি যদি @UpdateIDsএই পদ্ধতিতে টেবিলের পরিবর্তনশীল ব্যবহার করতে চান তবে আগত সারিগুলি টেম্প টেবিলের মধ্যে না ফেলেও আপনি পালাতে পারবেন।

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