নির্দিষ্ট কলামগুলির কোনও আপডেট হয়েছে কিনা তা পরীক্ষা করতে কীভাবে COLUMNS_UPDATED ব্যবহার করবেন?


13

আমার কাছে টেবিল রয়েছে ৪২ টি কলাম এবং একটি ট্রিগার যা কিছু স্টাফ করা উচিত যখন এই কলামগুলির 38 টি আপডেট করা হয়। সুতরাং, 4 টি কলাম পরিবর্তন করা থাকলে আমার যুক্তিটি এড়িয়ে যেতে হবে।

আমি আপডেট () ফাংশনটি ব্যবহার করতে পারি এবং একটি বড় IFশর্ত তৈরি করতে পারি, তবে আরও ছোট কিছু করতে পছন্দ করি। COLUMNS_UPDATED ব্যবহার করে আমি পরীক্ষা করতে পারি যে কিছু নির্দিষ্ট কলামগুলি আপডেট হয়েছে?

উদাহরণস্বরূপ, 3, 5 এবং 9 কলামটি আপডেট হয়েছে কিনা তা পরীক্ষা করে দেখুন:

  IF 
  (
    (SUBSTRING(COLUMNS_UPDATED(),1,1) & 20 = 20)
     AND 
    (SUBSTRING(COLUMNS_UPDATED(),2,1) & 1 = 1) 
  )
    PRINT 'Columns 3, 5 and 9 updated';

এখানে চিত্র বর্ণনা লিখুন

সুতরাং, মান 20কলামের জন্য 3এবং 5এবং মূল্য 1কলামের জন্য 9কারণ এটি দ্বিতীয় বাইট প্রথম কিছুক্ষনের মধ্যে সেট করা হয়। আমি যদি বিবৃতিটি এতে পরিবর্তন করি ORতবে কলামগুলি 3এবং 5বা কলামটি 9আপডেট হয়েছে কিনা তা পরীক্ষা করবে ?

কিভাবে ORএকটি বাইট প্রসঙ্গে যুক্তি প্রয়োগ করতে পারেন ?


7
ঠিক আছে, আপনি কি জানতে চান যে সেই কলামগুলি SETতালিকায় উল্লেখ করা হয়েছে , বা মানগুলি আসলে পরিবর্তিত হয়েছে কিনা? উভয় UPDATEএবং COLUMNS_UPDATED()শুধুমাত্র আপনাকে প্রাক্তনকে বলে। মানগুলি আসলে পরিবর্তন হয়েছে কিনা তা যদি আপনি জানতে চান তবে আপনাকে insertedএবং এর একটি সঠিক তুলনা করতে হবে deleted
অ্যারন বারট্র্যান্ড

প্রত্যাশিত SUBSTRINGফর্মটি বিভক্ত করার পরিবর্তে COLUMNS_UPDATED()আপনার ডকুমেন্টেশনে যেমন দেখানো হয়েছে তেমন কিছুটা তুলনা ব্যবহার করা উচিত । আপনি যদি কোনওভাবে টেবিলটি পরিবর্তন করেন তবে ফিরে আসা মানগুলির ক্রম COLUMNS_UPDATED()পরিবর্তন হবে Be
ম্যাক্স ভার্নন

@ অ্যারোনবার্ট্র্যান্ডের প্রতি শ্রদ্ধা হিসাবে, আপনার যদি কোনও মান SETবা UPDATEবিবৃতি ব্যবহার করে স্পষ্টভাবে আপডেট না করা সত্ত্বেও পরিবর্তিত হওয়া মানগুলি দেখার দরকার হয় , আপনি প্রশ্নে থাকা কলামগুলি ব্যবহার করতে CHECKSUM()বা BINARY_CHECKSUM(), এমনকি HASHBYTES()এটি দেখতে চাইতে পারেন ।
ম্যাক্স ভার্নন

উত্তর:


18

CHECKSUM()প্রকৃত মানগুলি পরিবর্তিত হয়েছে কিনা তা তুলনা করার জন্য আপনি মোটামুটি সহজ পদ্ধতি হিসাবে ব্যবহার করতে পারেন । CHECKSUM()উত্তীর্ণ হওয়া মানগুলির একটি তালিকা জুড়ে একটি চেকসাম তৈরি করবে, যার মধ্যে নম্বর এবং প্রকারটি অনির্দিষ্ট। সাবধান, এই জাতীয় চেকসামের সাথে তুলনা করার একটি ছোট সুযোগ রয়েছে যার ফলে মিথ্যা নেতিবাচক ফলাফল আসবে। আপনি যদি এটি মোকাবেলা করতে না পারেন, আপনি HASHBYTESপরিবর্তে 1 ব্যবহার করতে পারেন ।

নীচের উদাহরণটি কেবলমাত্র যদি কলামের মান বা কলামের পরিবর্তিত হয় তবে কেবলমাত্র টেবিলটিতে করা AFTER UPDATEপরিবর্তনের ইতিহাস ধরে রাখতে একটি ট্রিগার ব্যবহার করে । যদি পরিবর্তন হয়, কোনও পদক্ষেপ নেওয়া হয় না।TriggerTestData1 Data2Data3

USE tempdb;
IF COALESCE(OBJECT_ID('dbo.TriggerTest'), 0) <> 0
BEGIN
    DROP TABLE dbo.TriggerTest;
END
CREATE TABLE dbo.TriggerTest
(
    TriggerTestID INT NOT NULL
        CONSTRAINT PK_TriggerTest
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , Data1 VARCHAR(10) NULL
    , Data2 VARCHAR(10) NOT NULL
    , Data3 DATETIME NOT NULL
);

IF COALESCE(OBJECT_ID('dbo.TriggerResult'), 0) <> 0
BEGIN
    DROP TABLE dbo.TriggerResult;
END
CREATE TABLE dbo.TriggerResult
(
    TriggerTestID INT NOT NULL
    , Data1OldVal VARCHAR(10) NULL
    , Data1NewVal VARCHAR(10) NULL
    , Data2OldVal VARCHAR(10) NULL
    , Data2NewVal VARCHAR(10) NULL
);

GO
IF COALESCE(OBJECT_ID('dbo.TriggerTest_AfterUpdate'), 0) <> 0 
BEGIN
    DROP TRIGGER TriggerTest_AfterUpdate;
END
GO
CREATE TRIGGER TriggerTest_AfterUpdate
ON dbo.TriggerTest
AFTER UPDATE
AS 
BEGIN
    INSERT INTO TriggerResult
    (
        TriggerTestID
        , Data1OldVal
        , Data1NewVal
        , Data2OldVal
        , Data2NewVal
    )
    SELECT d.TriggerTestID
        , d.Data1
        , i.Data1
        , d.Data2
        , i.Data2
    FROM inserted i 
        LEFT JOIN deleted d ON i.TriggerTestID = d.TriggerTestID
    WHERE CHECKSUM(i.Data1, i.Data2) <> CHECKSUM(d.Data1, d.Data2);
END
GO

INSERT INTO dbo.TriggerTest (Data1, Data2, Data3)
VALUES ('blah', 'foo', GETDATE());

UPDATE dbo.TriggerTest 
SET Data1 = 'blah', Data2 = 'fee' 
WHERE TriggerTestID = 1;

SELECT *
FROM dbo.TriggerTest;

SELECT *
FROM dbo.TriggerResult

এখানে চিত্র বর্ণনা লিখুন

আপনি যদি COLUMNS_UPDATED () ফাংশনটি ব্যবহার করার জন্য জেদ করেন তবে আপনার প্রশ্নের মধ্যে থাকা কলামগুলির মূল মানটি হার্ড-কোড করা উচিত নয়, যেহেতু সারণির সংজ্ঞাটি বদলে যেতে পারে, যা হার্ড-কোডিং মানকে অকার্যকর করতে পারে। সিস্টেম টেবিলগুলি ব্যবহার করে রানটাইমের সময় মানটি কী হওয়া উচিত তা আপনি গণনা করতে পারেন। সচেতন থাকুন যে বিবৃতি দ্বারা প্রভাবিত কোনও সারিতে COLUMNS_UPDATED()কলামটি সংশোধন করা থাকলে প্রদত্ত কলাম বিটের জন্য ফাংশনটি সত্য প্রত্যাবর্তন করে । UPDATE TABLE

USE tempdb;
IF COALESCE(OBJECT_ID('dbo.TriggerTest'), 0) <> 0
BEGIN
    DROP TABLE dbo.TriggerTest;
END
CREATE TABLE dbo.TriggerTest
(
    TriggerTestID INT NOT NULL
        CONSTRAINT PK_TriggerTest
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , Data1 VARCHAR(10) NULL
    , Data2 VARCHAR(10) NOT NULL
    , Data3 DATETIME NOT NULL
);

IF COALESCE(OBJECT_ID('dbo.TriggerResult'), 0) <> 0
BEGIN
    DROP TABLE dbo.TriggerResult;
END
CREATE TABLE dbo.TriggerResult
(
    TriggerTestID INT NOT NULL
    , Data1OldVal VARCHAR(10) NULL
    , Data1NewVal VARCHAR(10) NULL
    , Data2OldVal VARCHAR(10) NULL
    , Data2NewVal VARCHAR(10) NULL
);

GO
IF COALESCE(OBJECT_ID('dbo.TriggerTest_AfterUpdate'), 0) <> 0 
BEGIN
    DROP TRIGGER TriggerTest_AfterUpdate;
END
GO
CREATE TRIGGER TriggerTest_AfterUpdate
ON dbo.TriggerTest
AFTER UPDATE
AS 
BEGIN
    DECLARE @ColumnOrdinalTotal INT = 0;

    SELECT @ColumnOrdinalTotal = @ColumnOrdinalTotal 
        + POWER (
                2 
                , COLUMNPROPERTY(t.object_id,c.name,'ColumnID') - 1
            )
    FROM sys.schemas s
        INNER JOIN sys.tables t ON s.schema_id = t.schema_id
        INNER JOIN sys.columns c ON t.object_id = c.object_id
    WHERE s.name = 'dbo'
        AND t.name = 'TriggerTest'
        AND c.name IN (
            'Data1'
            , 'Data2'
        );

    IF (COLUMNS_UPDATED() & @ColumnOrdinalTotal) > 0
    BEGIN
        INSERT INTO TriggerResult
        (
            TriggerTestID
            , Data1OldVal
            , Data1NewVal
            , Data2OldVal
            , Data2NewVal
        )
        SELECT d.TriggerTestID
            , d.Data1
            , i.Data1
            , d.Data2
            , i.Data2
        FROM inserted i 
            LEFT JOIN deleted d ON i.TriggerTestID = d.TriggerTestID;
    END
END
GO

--this won't result in rows being inserted into the history table
INSERT INTO dbo.TriggerTest (Data1, Data2, Data3)
VALUES ('blah', 'foo', GETDATE());

SELECT *
FROM dbo.TriggerResult;

এখানে চিত্র বর্ণনা লিখুন

--this will insert rows into the history table
UPDATE dbo.TriggerTest 
SET Data1 = 'blah', Data2 = 'fee' 
WHERE TriggerTestID = 1;

SELECT *
FROM dbo.TriggerTest;

SELECT *
FROM dbo.TriggerResult;

এখানে চিত্র বর্ণনা লিখুন

--this WON'T insert rows into the history table
UPDATE dbo.TriggerTest 
SET Data3 = GETDATE()
WHERE TriggerTestID = 1;

SELECT *
FROM dbo.TriggerTest;

SELECT *
FROM dbo.TriggerResult

এখানে চিত্র বর্ণনা লিখুন

--this will insert rows into the history table, even though only
--one of the columns was updated
UPDATE dbo.TriggerTest 
SET Data1 = 'blum' 
WHERE TriggerTestID = 1;

SELECT *
FROM dbo.TriggerTest;

SELECT *
FROM dbo.TriggerResult;

এখানে চিত্র বর্ণনা লিখুন

এই ডেমোটি ইতিহাস সারণিতে সারি সন্নিবেশ করায় যা সম্ভবত beোকানো উচিত নয়। সারিগুলির Data1কয়েকটি সারিগুলির জন্য তাদের কলামটি আপডেট হয়েছে এবং কিছু সারিটির Data3জন্য কলামটি আপডেট করা হয়েছে। যেহেতু এটি একটি একক বিবৃতি, সমস্ত সারি ট্রিগার মাধ্যমে একক পাস দ্বারা প্রক্রিয়া করা হয়। যেহেতু কিছু সারি Data1আপডেট হয়েছে, যা COLUMNS_UPDATED()তুলনার অংশ, তাই ট্রিগার দ্বারা দেখা সমস্ত সারি TriggerHistoryটেবিলের মধ্যে areোকানো হয়েছে । যদি এটি আপনার দৃশ্যের জন্য "ভুল" হয় তবে আপনার কার্সার ব্যবহার করে প্রতিটি সারি আলাদাভাবে পরিচালনা করতে হবে।

INSERT INTO dbo.TriggerTest (Data1, Data2, Data3)
SELECT TOP(10) LEFT(o.name, 10)
    , LEFT(o1.name, 10)
    , GETDATE()
FROM sys.objects o
    , sys.objects o1;

UPDATE dbo.TriggerTest 
SET Data1 = CASE WHEN TriggerTestID % 6 = 1 THEN Data2 ELSE Data1 END
    , Data3 = CASE WHEN TriggerTestID % 6 = 2 THEN GETDATE() ELSE Data3 END;

SELECT *
FROM dbo.TriggerTest;

SELECT *
FROM dbo.TriggerResult;

TriggerResultটেবিল এখন কিছু সম্ভাব্য বিভ্রান্তিকর সারি যে চেহারা মত তাদের সে অধিকার না যেহেতু অন্তর্গত তারা একেবারে (যে টেবিলের দুই কলাম পর্যন্ত) কোনো পরিবর্তন দেন হয়েছে। নীচের চিত্রের সারিগুলির 2 য় সেটটিতে, ট্রিগারস্টেস্টআইডি 7 কেবলমাত্র এটির মতো পরিবর্তিত হয়েছে বলে মনে হচ্ছে। অন্যান্য সারিগুলিতে কেবল Data3কলামটি আপডেট হয়েছিল; তবে যেহেতু ব্যাচের এক সারিটি Data1আপডেট হয়েছিল, সমস্ত সারিটি TriggerResultসারণিতে সন্নিবেশ করা হয়েছে ।

এখানে চিত্র বর্ণনা লিখুন

পর্যায়ক্রমে @ অ্যারনবার্ট্র্যান্ড এবং @ শ্রুতজকির নির্দেশ অনুসারে, আপনি insertedএবং deletedভার্চুয়াল টেবিলগুলিতে প্রকৃত ডেটার তুলনা করতে পারবেন । যেহেতু উভয় টেবিলের কাঠামোটি অভিন্ন, আপনি EXCEPTসারিগুলি ক্যাপচার করতে ট্রিগারটিতে একটি ধারা ব্যবহার করতে পারেন যেখানে আপনি আগ্রহী সঠিক কলামগুলি পরিবর্তিত হয়েছে:

IF COALESCE(OBJECT_ID('dbo.TriggerTest_AfterUpdate'), 0) <> 0 
BEGIN
    DROP TRIGGER TriggerTest_AfterUpdate;
END
GO
CREATE TRIGGER TriggerTest_AfterUpdate
ON dbo.TriggerTest
AFTER UPDATE
AS 
BEGIN
    ;WITH src AS
    (
        SELECT d.TriggerTestID
            , d.Data1
            , d.Data2
        FROM deleted d
        EXCEPT 
        SELECT i.TriggerTestID
            , i.Data1
            , i.Data2
        FROM inserted i
    )
    INSERT INTO dbo.TriggerResult 
    (
        TriggerTestID, 
        Data1OldVal, 
        Data1NewVal, 
        Data2OldVal, 
        Data2NewVal
    )
    SELECT i.TriggerTestID
        , d.Data1
        , i.Data1
        , d.Data2
        , i.Data2
    FROM inserted i 
        INNER JOIN deleted d ON i.TriggerTestID = d.TriggerTestID
END
GO

1 - দেখুন এইচটিএসএইচবিআইটিইএস গণনাটি সংঘর্ষেরও কারণ হতে পারে এমন ছোট্ট সুযোগের বর্জন করার জন্য /programming/297960/hash-collision- কি-are-the- সুযোগগুলি দেখুন। প্রেসিংয়ের পাশাপাশি এই সমস্যার একটি শালীন বিশ্লেষণ রয়েছে।


2
এটি ভাল তথ্য, তবে "আপনি যদি এটির সাথে ডিল করতে না পারেন, আপনি HASHBYTESপরিবর্তে ব্যবহার করতে পারেন ।" বিভ্রান্তিকর। এটা সত্য যে HASHBYTESহয় সম্ভাবনা কম চেয়ে মিথ্যা নেগেটিভ আছে CHECKSUM(ব্যবহৃত আলগোরিদিম আকারের উপর সম্ভাবনা তারতম্য), কিন্তু এটা শাসিত আউট করা যাবে না। যে কোনও হ্যাশিং ফাংশনটিতে সর্বদা সংঘর্ষের সম্ভাবনা থাকবে কারণ এটি তথ্য হ্রাস করার যথেষ্ট সম্ভাবনা রয়েছে। কোনও পরিবর্তনের বিষয়ে নিশ্চিত হওয়ার একমাত্র উপায় হ'ল স্ট্রিং ডেটা হলে টেক্সট INSERTEDএবং DELETEDটেবিলগুলি তুলনা করা এবং কোনও _BIN2কোলেশন ব্যবহার করা । হ্যাশগুলির সাথে তুলনা করা কেবলমাত্র পার্থক্যের জন্য নিশ্চিততা দেয়।
সলোমন রুটজকি

2
@ শ্রুতজকি যদি আমরা সংঘর্ষের বিষয়ে উদ্বিগ্ন হতে চলে আসি তবে এর সম্ভাবনাও জানাতে পারি। stackoverflow.com/questions/297960/…
ডেভ

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

1
পাদটীকা (+1) লাগানোর জন্য ধন্যবাদ। ব্যক্তিগতভাবে, আমি সেই নির্দিষ্ট উত্তর ছাড়া অন্য কোনও সংস্থান ব্যবহার করব কারণ এটি অত্যধিক সরল। দুটি সমস্যা রয়েছে: 1) উত্স মানের আকারগুলি বড় হওয়ার সাথে সাথে সম্ভাবনা বৃদ্ধি পায়। আমি গত রাতে এসও এবং অন্যান্য সাইটে বেশ কয়েকটি পোস্ট পড়েছি এবং ছবিতে এটি ব্যবহার করা একজন ব্যক্তি ২৫,০০০ এন্ট্রির পরে সংঘর্ষের কথা জানিয়েছেন, এবং ২) সম্ভাবনা কেবল তাই, আপেক্ষিক ঝুঁকি, হ্যাশ ব্যবহার করে কেউ তা করবে না বলে কিছু নেই 10 কে এন্ট্রিগুলিতে কয়েকবার সংঘর্ষের মধ্যে পড়ে। সম্ভাবনা = ভাগ্য। ভাগ্য ;-)- এর বিষয়ে আপনি যদি সচেতন হন তবে ভরসা করা ঠিক আছে।
সলোমন রুটজকি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.