2009-08-08 19 views
22

मेरे पास TSQL फ़ंक्शन अपडेट के बारे में कोई प्रश्न है। उदाहरण के लिए, मेरे पास फ़ील्ड नाम वाला एक टेबल है।टीएसक्यूएल ट्रिगर में अपडेट फ़ंक्शन

if Update(Name) 
    Begin 
    -- process 
    End 

अद्यतन अभी भी सच वापसी करेंगे भले ही नाम नहीं बदला गया है: अगर क्षेत्र का नाम बदल दिया गया है या नहीं एक अद्यतन ट्रिगर करने के बाद यह पसंद में अगर मैं जाँच? निम्नलिखित अद्यतन बयान एक ही मूल्य के साथ इसे अपडेट कर देगा:

SELECT @v_Name = Name From MyTable Where Id = 1; 
    Update MyTable Set Name = @v_Name where Id = 1; 

अद्यतन() सही रिटर्न भी नाम का मान परिवर्तित करना नहीं है, तो मैं करने के लिए डाला जाता है और नष्ट कर दिया आभासी तालिकाओं में मूल्य की तुलना करने की क्या ज़रूरत है पता लगाएं कि मूल्य वास्तव में बदल गया है या नहीं?

वैसे, डाले गए और हटाए गए वर्चुअल टेबल हैं और उनमें डेटा की एक से अधिक पंक्तियां हो सकती हैं यदि डेटा की एक से अधिक पंक्तियां एक TSQL INSERT या UPDATE कथन द्वारा बदली जाती हैं। एक से अधिक रिकॉर्ड्स के मामले में, पंक्तियों की गिनती संख्याएं डाली गई वर्चुअल टेबल में समान हैं और अपडेट (नाम) का असली अर्थ सत्य के रूप में क्या है? क्या इसका मतलब यह है कि कम से कम एक बदल गया है? या अद्यतन (नाम) का अर्थ है कि नाम का क्षेत्र अपडेट स्टेटमेंट द्वारा सेट किया गया है भले ही मूल्य बदल गया हो?

एसक्यूएल सर्वर का उपयोग मैं, सच हो सकता है Microsoft SQL 2005

उत्तर

20

UPDATE(), भले ही वह एक ही मूल्य है। मैं व्यक्तिगत रूप से इस पर भरोसा नहीं करता और मूल्यों की तुलना करता हूं।

दूसरा, DELETED और INSERTED की समान संख्या में पंक्तियां हैं।

अद्यतन() फ़ंक्शन प्रति पंक्ति नहीं है, लेकिन सभी पंक्तियों में है। इसका उपयोग करने का एक अन्य कारण नहीं है।

More here in MSDN, हालांकि यह वास्तव में थोड़ा सा स्पैस है।

टिप्पणी के बाद:

IF EXISTS (
    SELECT 
     * 
    FROM 
     INSERTED I 
     JOIN 
     DELETED D ON I.key = D.key 
    WHERE 
     D.valuecol <> I.valuecol --watch for NULLs! 
    ) 
    blah 
+0

धन्यवाद। परिवर्तन खोजने के लिए SQL सर्वर से उपलब्ध कोई अन्य विकल्प? या मुझे परिवर्तन खोजने के लिए प्रत्येक पंक्ति के माध्यम से लूप करना होगा? –

+1

बस कुंजी पर 2 टेबल में शामिल हों, कोई loosp – gbn

+1

आवश्यक है जिसे 'हटाया गया' और '** डाला ** ** वर्चुअल टेबल होना चाहिए, है ना? मैंने कभी "अद्यतन" वर्चुअल टेबल –

0

अद्यतन ट्रिगर सभी अद्यतन बयान पर सक्रिय होगा। प्रभावित पंक्तियां "सम्मिलित" और "हटाए गए" तालिकाओं में ट्रिगर के भीतर उपलब्ध हैं। आप दो तालिकाओं में पीके कॉलम की तुलना करके पुराने और नए मानों की तुलना कर सकते हैं (यदि आपके पास पीके है)। वास्तविक तालिका ट्रिगर निष्पादन समाप्त होने तक अपरिवर्तित बनी हुई है।

+0

ट्रिगर समाप्त होने के बाद तक "वास्तविक" तालिका अपरिवर्तित कहने के लिए गलत है। यदि ट्रिगर एक ट्रिगर के बाद होता है तो तालिका को ट्रिगर कहा जाता है जब तक अद्यतन किया जाएगा। बाहरी वातावरण केवल ट्रिगर (ओं) समाप्त होने पर अपडेट को देखेगा। –

33

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

यहां एक नमूना तालिका है ...

CREATE TABLE tblSample 
(
    SampleID INT PRIMARY KEY, 
    SampleName VARCHAR(10), 
    SampleNameLastChangedDateTime DATETIME, 
    Parent_SampleID INT 
) 

निम्नलिखित एसक्यूएल इस तालिका के खिलाफ इस्तेमाल किया गया था:

UPDATE tblSample SET SampleName = 'hello' 

..और एक इसके बाद सम्मिलित करें, अद्यतन ट्रिगर, इस विशेष प्रभाव में था SQL कथन हमेशा निम्नानुसार अद्यतन कार्य का मूल्यांकन करेगा ...

IF UPDATE(SampleName) --aways evaluates to TRUE 
IF UPDATE(SampleID) --aways evaluates to FALSE 
IF UPDATE(Parent_SampleID) --aways evaluates to FALSE 

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

यहाँ सिंक में एक अंतिम अपडेट स्तंभ रखने के लिए एक दृष्टिकोण है:

--/* 
IF OBJECT_ID('dbo.tgr_tblSample_InsertUpdate', 'TR') IS NOT NULL 
    DROP TRIGGER dbo.tgr_tblSample_InsertUpdate 
GO 
--*/ 

CREATE TRIGGER dbo.tgr_tblSample_InsertUpdate ON dbo.tblSample 
    AFTER INSERT, UPDATE 
AS 
BEGIN --Trigger 

    IF UPDATE(SampleName) 
    BEGIN 
     UPDATE tblSample SET 
     SampleNameLastChangedDateTime = CURRENT_TIMESTAMP 
     WHERE 
     SampleID IN (SELECT Inserted.SampleID 
       FROM Inserted LEFT JOIN Deleted ON Inserted.SampleID = Deleted.SampleID 
       WHERE COALESCE(Inserted.SampleName, '') <> COALESCE(Deleted.SampleName, '')) 
    END 

END --Trigger 

तर्क निर्धारित करने के लिए करता है, तो पंक्ति अद्यतन किया गया था कहां खंड में ऊपर है। यह वास्तविक चेक है जो आपको करने की ज़रूरत है। मेरा तर्क कुल मूल्यों और INSERTS को संभालने के लिए COALESCE का उपयोग कर रहा है।

... 
WHERE 
    SampleID IN (SELECT Inserted.SampleID 
       FROM Inserted LEFT JOIN Deleted ON Inserted.SampleID = Deleted.SampleID 
       WHERE COALESCE(Inserted.SampleName, '') <> COALESCE(Deleted.SampleName, '')) 

ध्यान दें कि यदि अद्यतन() की जांच जब SampleName स्तंभ नहीं अद्यतन किया जा रहा है के लिए ट्रिगर की दक्षता में सुधार करने में मदद करने के लिए इस्तेमाल किया जाता है। यदि किसी SQL कथन ने उदाहरण के लिए Parent_SampleID कॉलम को अद्यतन किया है, तो अगर अद्यतन (नमूना नाम) चेक उस IF कथन में अधिक जटिल तर्क के आसपास सेडस्टेप में मदद करेगा, जब इसे चलाने की आवश्यकता नहीं है। UPDATE() का उपयोग करने पर विचार करें जब यह उचित है लेकिन गलत कारण के लिए नहीं।

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

INSERT INTO tblSample 
(
    SampleID, 
    SampleName 
) 
SELECT 1, 'One' 
UNION SELECT 2, 'Two' 
UNION SELECT 3, 'Three' 

GO 
SELECT SampleID, SampleName, SampleNameLastChangedDateTime FROM tblSample 

/* 
SampleID SampleName SampleNameLastChangedDateTime 
----------- ---------- ----------------------------- 
1  One 2010-10-27 14:52:42.567 
2  Two 2010-10-27 14:52:42.567 
3  Three 2010-10-27 14:52:42.567 
*/ 

GO 

INSERT INTO tblSample 
(
    SampleID, 
    SampleName 
) 
SELECT 4, 'Foo' 
UNION SELECT 5, 'Five' 

GO 
SELECT SampleID, SampleName, SampleNameLastChangedDateTime FROM tblSample 
/* 
SampleID SampleName SampleNameLastChangedDateTime 
----------- ---------- ----------------------------- 
1  One 2010-10-27 14:52:42.567 
2  Two 2010-10-27 14:52:42.567 
3  Three 2010-10-27 14:52:42.567 
4  Foo 2010-10-27 14:52:42.587 
5  Five 2010-10-27 14:52:42.587 
*/ 

GO 

UPDATE tblSample SET SampleName = 'Foo' 

SELECT SampleID, SampleName, SampleNameLastChangedDateTime FROM tblSample 
/* 
SampleID SampleName SampleNameLastChangedDateTime 
----------- ---------- ----------------------------- 
1  Foo 2010-10-27 14:52:42.657 
2  Foo 2010-10-27 14:52:42.657 
3  Foo 2010-10-27 14:52:42.657 
4  Foo 2010-10-27 14:52:42.587 
5  Foo 2010-10-27 14:52:42.657 
*/ 
GO 

UPDATE tblSample SET SampleName = 'Not Prime' WHERE SampleID IN (1,4) 

SELECT SampleID, SampleName, SampleNameLastChangedDateTime FROM tblSample 
/* 
SampleID SampleName SampleNameLastChangedDateTime 
----------- ---------- ----------------------------- 
1  Not Prime 2010-10-27 14:52:42.680 
2  Foo  2010-10-27 14:52:42.657 
3  Foo  2010-10-27 14:52:42.657 
4  Not Prime 2010-10-27 14:52:42.680 
5  Foo  2010-10-27 14:52:42.657 
*/ 

--Clean up... 
DROP TRIGGER dbo.tgr_tblSample_InsertUpdate 
DROP TABLE tblSample 

उपयोगकर्ता GBN निम्नलिखित सुझाव दिया था:

IF EXISTS (
    SELECT 
     * 
    FROM 
     INSERTED I 
     JOIN 
     DELETED D ON I.key = D.key 
    WHERE 
     D.valuecol <> I.valuecol --watch for NULLs! 
    ) 
    blah 

एक IF (का उपयोग करने का GBN के सुझाव मौजूद है (... खंड और डाल

यहाँ कुछ एसक्यूएल ऊपर ट्रिगर के साथ खेलने के लिए है अगर I पंक्ति में मौजूद पंक्तियां मौजूद हैं तो तर्क बदल सकता है। यह दृष्टिकोण ट्रिगर में शामिल सभी पंक्तियों के लिए आग लग जाएगा, भले ही केवल कुछ पंक्तियों को वास्तव में बदला गया हो (जो आपके समाधान के लिए उपयुक्त हो सकता है, लेकिन हो सकता है उपयुक्त अगर आप केवल कुछ करना चाहते हैं पंक्तियों को हिंग करना जहां मूल्य बदल गए।) यदि आपको पंक्तियों में कुछ करने की आवश्यकता है जहां वास्तविक परिवर्तन हुआ है, तो आपको प्रदान किए गए अपने एसक्यूएल में अलग-अलग तर्क की आवश्यकता है।

ऊपर दिए गए मेरे उदाहरणों में, जब अद्यतन tblSample SET नमूना नाम = 'फू' कथन जारी किया गया है और चौथी पंक्ति पहले से ही 'foo' है, तो "अंतिम बदले गए डेटाटाइम" कॉलम को अपडेट करने के लिए जीबीएन के दृष्टिकोण का उपयोग करके चौथी पंक्ति भी अपडेट होगी , जो इस मामले में उपयुक्त नहीं होगा।

5

मैं यह निर्धारित करने का सबसे अच्छा तरीका मानता हूं कि कॉलम मान वास्तव में बदल गया है (जैसा कि उसी मान के साथ अपडेट किया जा रहा है) हटाए गए और सम्मिलित छद्म तालिकाओं में कॉलम मानों की तुलना करना है। हालांकि, यदि आप कुछ कॉलम से अधिक जांचना चाहते हैं तो यह वास्तविक दर्द हो सकता है।

यहां एक चाल है जिसे मैं बनाए रख रहा था (मूल लेखक को नहीं जानता): एक कॉलिंग के साथ एक यूनियन और ग्रुप बाय का उपयोग करके यह निर्धारित करने के लिए कि कौन से कॉलम बदल गए हैं।

जैसे, ट्रिगर में, पंक्तियों की आईडी को बदल दिया है कि के के प्राप्त करने के लिए:

SELECT SampleID 
FROM 
    (
     SELECT SampleID, SampleName 
     FROM deleted 

     -- NOTE: UNION, not UNION ALL. UNION by itself removes duplicate 
     -- rows. UNION ALL includes duplicate rows. 
     UNION 

     SELECT SampleID, SampleName 
     FROM inserted 
    ) x 
GROUP BY SampleID 
HAVING COUNT(*) > 1 

यह बहुत ज्यादा काम है जब आप केवल जाँच कर रहे हैं अगर एक एकल स्तंभ बदल गया है। लेकिन अगर आप 10 या 20 कॉलम की जाँच कर रहे यूनिअन विधि की तुलना में

WHERE COALESCE(Inserted.Column1, '') <> COALESCE(Deleted.Column1, '') 
    OR COALESCE(Inserted.Column2, '') <> COALESCE(Deleted.Column2, '') 
    OR COALESCE(Inserted.Column3, '') <> COALESCE(Deleted.Column3, '') 
    OR ... 
2

मुझे लगता है कि निम्नलिखित कोड ऊपर क्योंकि यह कॉलम आप में जाँच करना चाहते हैं पर केंद्रित है उदाहरण से बेहतर है एक बहुत कम काम है एक संक्षिप्त और कुशल तरीके से।

यह निर्धारित करता है कि निर्दिष्ट केवल कॉलम में कोई मान बदल गया है या नहीं। मैंने अन्य समाधानों की तुलना में अपने प्रदर्शन की जांच नहीं की है लेकिन यह मेरे डेटाबेस में अच्छी तरह से काम कर रहा है।

यह बाएं क्वेरी से किसी भी पंक्ति को वापस करने के लिए EXCEPT सेट ऑपरेटर का उपयोग करता है जो सही क्वेरी पर नहीं मिलता है। इस कोड का उपयोग INSERT और अद्यतन ट्रिगर्स में किया जा सकता है।

"प्राथमिककिड" कॉलम तालिका की प्राथमिक कुंजी है (एकाधिक कॉलम हो सकता है) और दोनों सेटों के बीच मिलान सक्षम करने के लिए आवश्यक है।

-- Only do trigger logic if specific field values change. 
IF EXISTS(SELECT PrimaryKeyID 
       ,Column1 
       ,Column7 
       ,Column10 
      FROM inserted 
      EXCEPT 
      SELECT PrimaryKeyID 
       ,Column1 
       ,Column7 
       ,Column10 
      FROM deleted) -- Tests for modifications to fields that we are interested in 
BEGIN 
      -- Put code here that does the work in the trigger 

END 

आप बाद में ट्रिगर तर्क में परिवर्तित पंक्तियों उपयोग करना चाहते हैं, मैं आमतौर पर छोड़कर पूछताछ के परिणामों एक मेज चर कि बाद में संदर्भित किया जा सकता में डाल दिया।

मुझे आशा है कि यह ब्याज की बात है :-)

संबंधित मुद्दे