কে মোছার ট্রিগারে রেকর্ড মুছেছে সে সম্পর্কে তথ্য প্রেরণ করা


11

নিরীক্ষণের ট্রেইল সেট আপ করার সময় আমার কোনও টেবিলে রেকর্ড আপডেট করা বা রেকর্ড করা হচ্ছে তা ট্র্যাক করার কোনও সমস্যা নেই তবে যাইহোক, কে রেকর্ড মুছে ফেলে তা ট্র্যাকিং আরও সমস্যাযুক্ত বলে মনে হয়।

"আপডেটডাই" দ্বারা সন্নিবেশ / আপডেট ক্ষেত্রটি অন্তর্ভুক্ত করে আমি সন্নিবেশ / আপডেটগুলি ট্র্যাক করতে পারি। এটি INSERT / UPDATE ট্রিগারটিকে "আপডেটডাই বাই" এর মাধ্যমে ফিল্ডটিতে অ্যাক্সেসের অনুমতি দেয় inserted.UpdatedBy। তবে মুছুন ট্রিগার দিয়ে কোনও তথ্য sertedোকানো / আপডেট করা হয় না। মুছে ফেলার ট্রিগারটিতে এমন কী তথ্য পাঠানোর কোনও উপায় আছে যাতে এটি জানতে পারে যে রেকর্ডটি মোছা হয়েছে?

এখানে একটি সন্নিবেশ / আপডেট ট্রিগার রয়েছে

ALTER TRIGGER [dbo].[trg_MyTable_InsertUpdate] 
ON [dbo].[MyTable]
FOR INSERT, UPDATE
AS  

INSERT INTO AuditTable (IdOfRecordedAffected, UserWhoMadeChanges) 
VALUES (inserted.ID, inserted.LastUpdatedBy)
FROM inserted 

এসকিউএল সার্ভার 2012 ব্যবহার করা


1
এই উত্তর দেখুন । SUSER_SNAME()কে হ'ল রেকর্ডটি মুছে ফেলার চাবি।
কিন শাহ

1
ধন্যবাদ কিন, তবে আমি মনে করি না যে SUSER_SNAME()কোনও ওয়েব অ্যাপ্লিকেশনের মতো পরিস্থিতিতে কাজ করবে যেখানে কোনও একক ব্যবহারকারীর পুরো অ্যাপ্লিকেশনটির জন্য ডাটাবেস যোগাযোগের জন্য ব্যবহৃত হতে পারে।
ওয়েবওয়ার্ম

1
আপনি উল্লেখ করেননি যে আপনি একটি ওয়েব অ্যাপ্লিকেশন কল করছেন।
কিন শাহ

দুঃখিত আত্মীয়, আমি অ্যাপ্লিকেশন ধরণের আরও নির্দিষ্ট করা উচিত ছিল।
ওয়েবওয়ার্ম

উত্তর:


10

মুছে ফেলার ট্রিগারটিতে এমন কী তথ্য পাঠানোর কোনও উপায় আছে যাতে এটি জানতে পারে যে রেকর্ডটি মোছা হয়েছে?

হ্যাঁ: বলা হয় খুব শীতল (এবং ব্যবহৃত বৈশিষ্ট্যের অধীনে) ব্যবহার করে CONTEXT_INFO। এটি মূলত সেশন মেমরি যা সমস্ত স্কোপে বিদ্যমান এবং লেনদেন দ্বারা আবদ্ধ নয়। এটি তথ্য (যে কোনও তথ্য - ভাল, যে কোনও সীমাবদ্ধ স্থানের সাথে ফিট করে) পাশাপাশি ট্রি-ট্র্যাকগুলিতে সাব-প্রোক / এক্সইসি কলগুলির মধ্যে পিছনে পিছনে ব্যবহৃত হতে পারে। এবং আমি এই একই একই পরিস্থিতির জন্য আগে ব্যবহার করেছি।

  • প্রসঙ্গ তথ্য একটি ভারবিনারি (128)

  • এর মাধ্যমে সেট করুন: SET CONTEXT_INFO

  • এর মাধ্যমে পান: CONTEXT_INFO ()

এটি কীভাবে কাজ করে তা দেখতে নীচের সাথে পরীক্ষা করুন। লক্ষ্য করুন যে আমি এর CHAR(128)আগে রূপান্তর করছি CONVERT(VARBINARY(128), ..। এটি খালি প্যাডিংকে বাধ্যতামূলক করা VARCHARযখন এটি এস CONTEXT_INFO()থেকে VARBINARY(128)ডান-প্যাডযুক্ত হওয়ার পরে এটি থেকে বেরিয়ে আসার সময় সহজেই রূপান্তর করা সহজ 0x00

SELECT CONTEXT_INFO();
-- Initially = NULL

DECLARE @EncodedUser VARBINARY(128);
SET @EncodedUser = CONVERT(VARBINARY(128),
                            CONVERT(CHAR(128), 'I deleted ALL your records! HA HA!')
                          );
SET CONTEXT_INFO @EncodedUser;

SELECT CONTEXT_INFO() AS [RawContextInfo],
       RTRIM(CONVERT(VARCHAR(128), CONTEXT_INFO())) AS [DecodedUser];

ফলাফল:

0x492064656C6574656420414C4C20796F7572207265636F7264732120484120484121202020202020...
I deleted ALL your records! HA HA!

সবগুলোকে একত্রে রাখ:

  1. অ্যাপ্লিকেশনটিকে এমন একটি "মুছুন" সঞ্চিত পদ্ধতি কল করতে হবে যা রেকর্ডটিকে মুছে ফেলছে এমন ব্যবহারকারীর নাম (বা যা কিছু) পাস করে। আমি ধরে নিলাম এটি ইতিমধ্যে মডেলটি ব্যবহৃত হচ্ছে কারণ এটির মতো শোনা যাচ্ছে যে আপনি ইতিমধ্যে সন্নিবেশ এবং আপডেট ক্রিয়াকলাপগুলি ট্র্যাক করছেন।

  2. "মুছুন" সঞ্চিত পদ্ধতিটি করে:

    DECLARE @EncodedUser VARBINARY(128);
    SET @EncodedUser = CONVERT(VARBINARY(128),
                                CONVERT(CHAR(128), @UserName)
                              );
    SET CONTEXT_INFO @EncodedUser;
    
    -- DELETE STUFF HERE
  3. নিরীক্ষার ট্রিগারটি করে:

    -- Set the INT value in LEFT (currently 50) to the max size of [UserWhoMadeChanges]
    INSERT INTO AuditTable (IdOfRecordedAffected, UserWhoMadeChanges) 
       SELECT del.ID, COALESCE(
                         LEFT(RTRIM(CONVERT(VARCHAR(128), CONTEXT_INFO())), 50),
                         '<unknown>')
       FROM DELETED del;
  4. দয়া করে নোট করুন যে, @ সানগ্যালার্ডি একটি মন্তব্যে উল্লেখ করেছেন, অন্যান্য পদ্ধতি এবং / অথবা এই টেবিল থেকে রেকর্ডগুলি মোছার জন্য এইড-এইচ কোয়ের কারণে, সম্ভবত এটি হয়:

    • CONTEXT_INFOসেট করা হয়নি এবং এখনও আছে NULL:

      এই কারণে মানটি ডিফল্ট INSERT INTO AuditTableকরতে আমি ব্যবহার COALESCEকরতে উপরেরটি আপডেট করেছি । অথবা, যদি আপনি ডিফল্ট না চান এবং একটি নাম প্রয়োজন হয় তবে আপনি এর মতো কিছু করতে পারেন:

      DECLARE @UserName VARCHAR(50); -- set to the size of AuditTable.[UserWhoMadeChanges]
      SET @UserName = LEFT(RTRIM(CONVERT(VARCHAR(128), CONTEXT_INFO())), 50);
      
      IF (@UserName IS NULL)
      BEGIN
         ROLLBACK TRAN; -- cancel the DELETE operation
         RAISERROR('Please set UserName via "SET CONTEXT_INFO.." and try again.', 16 ,1);
      END;
      
      -- use @UserName in the INSERT...SELECT
    • CONTEXT_INFOযে একটি মান সেট করা হয়েছে না একটি বৈধ ব্যবহারকারী নাম, তাই মাপ অতিক্রম পারে AuditTable.[UserWhoMadeChanges]ক্ষেত্র:

      এই কারণে আমি একটি LEFTফাংশন যুক্ত করেছি তা নিশ্চিত করার জন্য যা যা ধরা পড়েছে CONTEXT_INFOতা ভেঙে না যায় INSERT। কোডটিতে উল্লিখিত হিসাবে, আপনাকে কেবল ক্ষেত্রের 50আসল আকারে সেট করতে হবে UserWhoMadeChanges


এসকিউএল সার্ভারের জন্য আপডেট ২০১ 2016 এবং আরও নতুন

এসকিউএল সার্ভার 2016 এই প্রতি সেশনের মেমরির একটি উন্নত সংস্করণ যুক্ত করেছে: সেশন প্রসঙ্গ। নতুন সেশন প্রসঙ্গটি মূলত "কী" টাইপ sysname(যেমন NVARCHAR(128)) এবং "মান" সত্তার সাথে কী-মান জোড়াগুলির একটি হ্যাশ টেবিল SQL_VARIANT। অর্থ:

  1. মানগুলির একটি বিচ্ছেদ এখন অন্যান্য ব্যবহারের সাথে বিরোধের সম্ভাবনা কম
  2. আপনি বিভিন্ন ধরণের সঞ্চয় করতে পারেন, এর মাধ্যমে মূল্যটি ফেরত পাওয়ার সময় অদ্ভুত আচরণ সম্পর্কে আর চিন্তা করার দরকার নেই (বিশদরCONTEXT_INFO() জন্য, দয়া করে আমার পোস্টটি দেখুন: কেন কনটেক্সT_INFO () সেট কনটেক্সT_INFO দ্বারা সুনির্দিষ্ট মানটি ফেরত দেয় না? )
  3. আপনি আরও অনেক বেশি স্থান পাবেন: "মান" প্রতি 8000 বাইট সর্বাধিক, সমস্ত কীগুলিতে 256kb অবধি (128 বাইট সর্বাধিকের তুলনায় CONTEXT_INFO)

বিশদ জন্য, নিম্নলিখিত ডকুমেন্টেশন পৃষ্ঠা দেখুন:


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

@ সানগ্যালার্ডি আপনি কি দয়া করে এই ঘটনার প্রকৃত উদাহরণ প্রদান করতে পারেন? সেশন == @@SPID। এটি পার-সেশন / সংযোগ মেমরি। একটি সেশন অন্য সেশনের প্রসঙ্গ তথ্য ওভাররাইট করতে পারে না। এবং সেশন লগ অফ যখন মান চলে যায়। "পূর্বে সেট করা আইটেম" বলে কোনও জিনিস নেই।
সলোমন রুটজকি

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

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

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

5

আপনি সেভাবে পারবেন না, যদি না আপনি কোনও অ্যাপ্লিকেশন স্তরের পরিবর্তে এসকিউএল সার্ভারের ব্যবহারকারী আইডি রেকর্ড করে থাকেন।

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


আমি আপনার পয়েন্ট দেখুন. আমি সত্যিই অ্যাপ্লিকেশন স্তরের ব্যবহারকারী লগ করতে খুঁজছি হবে।
ওয়েবওয়ার্ম

ডেভিড, আসলে আপনি ট্রিগারগুলিতে তথ্য পাস করতে পারেন। বিস্তারিত জানার জন্য দয়া করে আমার উত্তর দেখুন :)।
সলোমন রুটজকি

এখানে ভাল পরামর্শ, আমি এই পথটি সত্যিই পছন্দ করি। আসল মুছা ট্রিগার হিসাবে একই পদক্ষেপে কাকে ক্যাপচার করে দুটি পাখি হত্যা করে। যেহেতু এই কলামটি এই টেবিলের প্রতিটি রেকর্ডের জন্য নুল হতে চলেছে, মনে হচ্ছে এটি এসকিউএল সার্ভার SPARSEকলামটির ভাল ব্যবহার হবে ?
এয়ারএন 57575

2

মুছে ফেলার ট্রিগারটিতে এমন কী তথ্য পাঠানোর কোনও উপায় আছে যাতে এটি জানতে পারে যে রেকর্ডটি মোছা হয়েছে?

হ্যাঁ, দৃশ্যত দুটি উপায় রয়েছে ;-)। CONTEXT_INFOআমি এখানে আমার অন্যান্য উত্তরে যেমন পরামর্শ হিসাবে ব্যবহার করেছি সে সম্পর্কে যদি কোনও সংরক্ষণ থাকে তবে আমি অন্য একটি উপায় নিয়ে ভাবলাম যা অন্য কোড / প্রক্রিয়াগুলি থেকে একটি ক্লিনার ফাংশনাল বিচ্ছিন্নতা আছে: স্থানীয় অস্থায়ী টেবিলটি ব্যবহার করুন।

টেম্প টেবিলের নামটিতে সারণীর নামটি মুছে ফেলা উচিত কারণ এটি একই সেশনে চালিত হতে পারে এমন অন্য কোনও কোড থেকে পৃথক রাখতে সহায়তা করবে। এর লাইন ধরে কিছু:
#<TableName>DeleteAudit

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

  1. অ্যাপ্লিকেশনটিকে এমন একটি "মুছুন" সঞ্চিত পদ্ধতি কল করতে হবে যা রেকর্ডটিকে মুছে ফেলছে এমন ব্যবহারকারীর নাম (বা যা কিছু) পাস করে। আমি ধরে নিলাম এটি ইতিমধ্যে মডেলটি ব্যবহৃত হচ্ছে কারণ এটির মতো শোনা যাচ্ছে যে আপনি ইতিমধ্যে সন্নিবেশ এবং আপডেট ক্রিয়াকলাপগুলি ট্র্যাক করছেন।

  2. "মুছুন" সঞ্চিত পদ্ধতিটি করে:

    CREATE TABLE #MyTableDeleteAudit (UserName VARCHAR(50));
    INSERT INTO #MyTableDeleteAudit (UserName) VALUES (@UserName);
    
    -- DELETE STUFF HERE
  3. নিরীক্ষার ট্রিগারটি করে:

    -- Set the datatype and length to be the same as the [UserWhoMadeChanges] field
    DECLARE @UserName VARCHAR(50);
    IF (OBJECT_ID(N'tempdb..#TriggerTestDeleteAudit') IS NOT NULL)
    BEGIN
       SELECT @UserName = UserName
       FROM #TriggerTestDeleteAudit;
    END;
    
    -- catch the following conditions: missing table, no rows in table, or empty row
    IF (@UserName IS NULL OR @UserName NOT LIKE '%[a-z]%')
    BEGIN
      /* -- uncomment if undefined UserName == badness
       ROLLBACK TRAN; -- cancel the DELETE operation
       RAISERROR('Please set UserName via #TriggerTestDeleteAudit and try again.', 16 ,1);
       RETURN; -- exit
      */
      /* -- uncomment if undefined UserName gets default value
       SET @UserName = '<unknown>';
      */
    END;
    
    INSERT INTO AuditTable (IdOfRecordedAffected, UserWhoMadeChanges) 
       SELECT del.ID, @UserName
       FROM DELETED del;

    আমি এই কোডটি একটি ট্রিগারে পরীক্ষা করেছি এবং এটি প্রত্যাশার মতো কাজ করে।

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