2009-03-04 15 views
5

में पुरानी प्राथमिक कुंजी निर्धारित करें मैंने कहीं पहले यह किया है मुझे यकीन है कि मुझे यकीन है!एसक्यूएल ट्रिगर

मेरे पास एक SQL Server 2000 तालिका है जिसे मुझे अद्यतनों पर फ़ील्ड में लॉग इन करने और दूसरी लॉगिंग तालिका में सम्मिलित करने की आवश्यकता है। संरचना मैं उपयोग कर रहा हूँ का एक सरलीकृत संस्करण नीचे है:

MainTable 
ID varchar(10) PRIMARY KEY 
DESCRIPTION varchar(50) 

LogTable 
OLDID varchar(10) 
NEWID varchar(10) 

के लिए इस तरह किसी अन्य क्षेत्र में कुछ महान काम करेगा:

Select i.DESCRIPTION As New, d.DESCRIPTION As Old 
From Inserted i 
LEFT JOIN Deleted d On i.ID=d.ID 

... लेकिन स्पष्ट रूप से शामिल होने के विफल हो जाएगा यदि आईडी था बदला हुआ।

मैं टेबल्स को रास्ते में संशोधित नहीं कर सकता, इस डेटाबेस में मेरे पास एकमात्र शक्ति ट्रिगर बनाने के लिए है।

वैकल्पिक रूप से कोई ऐसा व्यक्ति है जो मुझे समय यात्रा सिखा सकता है और मैं अतीत में वापस जाऊंगा और खुद से पूछूंगा कि मैंने यह कैसे किया? चीयर्स :)


संपादित करें:

मैं मैं यहाँ कुछ चीजें स्पष्ट करने की आवश्यकता है। यह वास्तव में मेरा डेटाबेस नहीं है, यह एक पूर्व-विद्यमान प्रणाली है जिसका मेरे पास इस ट्रिगर को लिखने के अलावा लगभग कोई नियंत्रण नहीं है।

मेरा प्रश्न यह है कि अगर प्राथमिक कुंजी बदल दी गई तो मैं पुरानी प्राथमिक कुंजी कैसे प्राप्त कर सकता हूं। मुझे बताया गया है कि मैं प्राथमिक कुंजी परिवर्तन नहीं होना चाहिए या आदि विदेशी चाबी का पीछा करते हुए के बारे में है कि मेरी समस्या :)

+0

आप अपने प्राथमिक कुंजी को बदलने नहीं किया जाना चाहिए। –

+1

एक परिपूर्ण दुनिया में आप बिल्कुल सही हैं। हालांकि मैं डेटाबेस और एप्लिकेशन के बाहर काम कर रहा हूं जो इस तालिका का उपयोग करता है, इसलिए मैं उस नियम को लागू नहीं कर सकता – keith

उत्तर

2

यह ग्रहण करने के लिए संभव है कि डाला जाता है और हटा टेबल एक ट्रिगर में आपको प्रस्तुत किया गया एक ही क्रम में होने की गारंटी है?

+0

हम्म मेरे सीमित परीक्षण में हाँ, वे सही क्रम में थे, लेकिन फिर कर्सर (प्रदर्शन) का उपयोग करने का सवाल है, और यह विश्वसनीय है, अगर मैंने इसे कहीं दस्तावेज में देखा तो मैं बहुत खुश हूं :) – keith

+0

मैं गया अंत में और यह मेरी बेहद सीमित समस्या में मेरे लिए काम किया! – keith

3
DECLARE @OldKey int, @NewKey int; 

SELECT @Oldkey = [ID] FROM DELETED; 
SELECT @NewKey = [ID] FROM INSERTED; 

यह केवल काम करता है अगर आप एक ही पंक्ति नहीं है की जरूरत नहीं है। अन्यथा पुरानी और नई पंक्तियों को जोड़ने के लिए आपके पास "एंकर" नहीं है। तो INRERTED में> 1 के लिए अपने ट्रिगर में जांचें।

0

---- नया ---- मेज पर एक पहचान स्तंभ जोड़ने के उस आवेदन को बदल नहीं सकते, तो आप ट्रिगर के भीतर हटा तालिकाओं के लिए डाला शामिल होने के लिए है कि नए स्तंभ का उपयोग कर सकते हैं:

ALTER TABLE YourTableName ADD 
    PrivateID int NOT NULL IDENTITY (1, 1) 
GO 

---- पुराना ---- कभी भी महत्वपूर्ण मान अपडेट/परिवर्तित न करें। आप यह कैसे कर सकते हैं और अपनी सभी विदेशी कुंजी को ठीक कर सकते हैं?

मैं कभी भी ऐसे ट्रिगर का उपयोग करने की अनुशंसा नहीं करता जो पंक्तियों के एक सेट को संभाल नहीं सके।

यदि आपको कुंजी बदलनी है, तो उचित नई कुंजी और मानों के साथ एक नई पंक्ति डालें, SCOPE_IDENTITY() का उपयोग करें यदि आप यही कर रहे हैं। पुरानी पंक्ति हटाएं। पुरानी पंक्ति के लिए लॉग इन करें कि इसे नई पंक्ति की कुंजी में बदल दिया गया था, जो अब आपके पास होना चाहिए। मुझे उम्मीद है कि आपके लॉग में बदली हुई कुंजी पर कोई विदेशी कुंजी नहीं है ...

+0

आप जानते हैं कि @@ पहचान सही उपयोग करने के लिए एक बहुत ही बुरी चीज है? – HLGEM

+0

@ एचएलजीईएम, मैंने सोचा कि 2005 में SCOPE_IDENTITY() नया था, और सवाल 2000 के लिए था, लेकिन मैंने चेक किया और SCOPE_IDENTITY() 2000 में जोड़ा गया था, इसलिए मैंने इसे –

1

मुझे नहीं लगता कि यह संभव है। कल्पना कीजिए कि अगर आप तालिका में 4 पंक्तियों:

1 Val1 
2 Val2 
3 Val3 
4 Val4 

अब निम्न अद्यतन जारी:

UPDATE MainTable SET 
ID = CASE ID WHEN 1 THEN 2 WHEN 2 THEN 1 ELSE ID END 
Description = CASE ID WHEN 3 THEN 'Val4' WHEN 4 THEN 'Val3' ELSE Description END 

अब, आप क्या पंक्तियों 1 & 2 को क्या हुआ और क्या हुआ के बीच अंतर करने जा रहे हैं पंक्तियां 3 & 4।और सबसे महत्वपूर्ण बात यह है कि क्या आप वर्णन कर सकते हैं कि उनके बीच क्या अंतर है? सभी चीजें जो आपको बताती हैं कि कौन से कॉलम अपडेट किए गए हैं, आपकी मदद नहीं करेंगे।

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

+0

पर विचार करने के लिए बदल दिया, एक अच्छा विचार है, इंडेक्स का एक टन है मेज पर, लेकिन दुर्भाग्य से (प्राथमिक कुंजी के अलावा) कोई अद्वितीय अनुक्रमणिका नहीं है। – keith

1

यदि आपको एकाधिक-पंक्ति प्रविष्टियों/अपडेटों को संभालना होगा, और कोई वैकल्पिक कुंजी नहीं है जो बदलने की गारंटी नहीं है, तो ऐसा करने का एकमात्र तरीका यह है कि ट्रिगर के इंस्टॉलेशन का उपयोग करना है। उदाहरण के लिए, ट्रिगर में आप मूल प्रविष्टि/अपडेट कमांड को प्रति पंक्ति एक कमांड में तोड़ सकते हैं, प्रत्येक पुरानी आईडी को डालने/अपडेट करने से पहले।

0

SQL सर्वर में ट्रिगर्स के भीतर आपके पास दो तालिकाओं तक पहुंच है: हटाया और डाला गया। इन दोनों का पहले ही उल्लेख किया जा चुका है। यहाँ कैसे वे क्या कार्रवाई ट्रिगर पर फायरिंग कर रहा है पर ढंग से काम करते है:

सम्मिलित संचालन

  • नष्ट कर दिया - उपयोग नहीं किया
  • डाला - नई पंक्तियाँ तालिका
में जोड़ा जा रहा होता है

हटाएं ऑपरेशन

  • नष्ट कर दिया - शामिल पंक्तियों तालिका से हटाया जा रहा
  • डाला - उपयोग नहीं किया

अपडेट करें कार्रवाई

  • नष्ट कर दिया - के रूप में वे अद्यतन आपरेशन
  • से पहले मौजूद होगा पंक्तियां हैं
  • डाला गया - इसमें पंक्तियां शामिल हैं क्योंकि वे अद्यतन ऑपरेशन
के बाद मौजूद होंगे

ये फ़ंक्शन टेबल की तरह हर तरह से होते हैं। इसलिए, यह इस तरह के (ऑपरेशन, केवल लेखा परीक्षा की मेज पर मौजूद DateChanged करता है) निम्नलिखित की तरह कुछ के रूप में एक पंक्ति आधारित संचालन उपयोग करने के लिए पूरी तरह संभव है:

INSERT INTO MyAuditTable 
(ID, FirstColumn, SecondColumn, ThirdColumn, Operation, DateChanged) 
VALUES 
SELECT ID, FirstColumn, SecondColumn, ThirdColumn, 'Update-Before', GETDATE() 
FROM deleted 
UNION ALL 
SELECT ID, FirstColumn, SecondColumn, ThirdColumn, 'Update-After', GETDATE() 
FROM inserted 
0

आप तालिका मेनटेबल (उदाहरण के लिए correlationid के लिए नामित) पर एक नया पहचान कॉलम बना सकते हैं और इस कॉलम का उपयोग करके सम्मिलित और हटाए गए तालिकाओं को सहसंबंधित कर सकते हैं। यह नया कॉलम मौजूदा कोड के लिए पारदर्शी होना चाहिए।

INSERT INTO LOG(OLDID, NEWID) 
SELECT deleted.id AS OLDID, inserted.id AS NEWID 
FROM inserted 
INNER JOIN deleted 
    ON inserted.correlationid = deleted.correlationid 

ध्यान दें, आप लॉग तालिका में डुप्लिकेट रिकॉर्ड डाल सकते हैं।

+0

वास्तव में एक अच्छा विचार है जो वास्तव में काम करेगा, लेकिन इस सवाल में मैंने कहा था कि मैं किसी भी तरह से तालिका को बदल नहीं सकता – keith

+0

आप पहचान कॉलम के साथ एक नई तालिका बना सकते हैं, एक से एक को बनाए रखने योग्य और कैस्केड अपडेट के संबंध में, फिर उपयोग करें सम्मिलित और हटाए गए तालिकाओं में शामिल होने से पहले वर्णित दृष्टिकोण। यदि आप तालिका नहीं बना सकते हैं, तो आपको "ट्रिगर के बजाए" उपयोग करना चाहिए, लेकिन प्रदर्शन के मुद्दों को बड़े अपडेट में होना चाहिए क्योंकि ट्रिगर को प्रभावित प्रति पंक्ति में एक बार बुलाया जाएगा। मुझे उम्मीद है कि यह उपयोगी होगा –

0

बेशक किसी को मेज पर प्राथमिक कुंजी नहीं बदलनी चाहिए - लेकिन यह वही है जो ट्रिगर्स (भाग में) के लिए होना चाहिए, लोगों को उन चीजों को करने से रोकना है जो उन्हें नहीं करना चाहिए। यह ओरेकल या MySQL में एक ट्रिगर लिखने के लिए एक छोटा कार्य है जो प्राथमिक कुंजी में परिवर्तन को रोकता है और उन्हें रोकता है, लेकिन SQL सर्वर में बिल्कुल आसान नहीं है।

if exists 
    (
    select * 
    from inserted changed 
      join deleted old 
    where changed.rowID = old.rowID 
    and changed.id != old.id 
) 
... [roll it all back] 

यही वजह है लोगों ROWID की एसक्यूएल सर्वर बराबर के लिए googling बाहर जाना:

क्या निश्चित रूप से आप के लिए किया जाएगा करने के लिए सक्षम होने के लिए प्यार होता है बस कुछ इस तरह से करते हैं। खैर, एसक्यूएल सर्वर में यह नहीं है; तो आपको एक और दृष्टिकोण के साथ आना होगा।

एक तेज, लेकिन दुख की बात नहीं है कि बमप्रूफ, संस्करण अद्यतन ट्रिगर के बजाय एक लिखना है जो यह देखने के लिए दिखता है कि किसी भी डाली गई पंक्तियों में प्राथमिक कुंजी को अद्यतन तालिका में या इसके विपरीत नहीं मिला है या नहीं। यह सबसे पकड़ होता है, लेकिन सभी नहीं, त्रुटियों की:

if exists 
    (
    select * 
    from inserted lost 
      left join updated match 
      on match.id = lost.id 
    where match.id is null 
    union 
    select * 
    from deleted new 
      left join inserted match 
      on match.id = new.id 
    where match.id is null 
) 
    -- roll it all back 

लेकिन यह अभी भी एक अद्यतन की तरह पकड़ नहीं करता है ...

update myTable 
    set id = case 
       when id = 1 then 2 
       when id = 2 then 1 
       else id 
       end 

अब, मैं इस धारणा बनाने की कोशिश की है कि सम्मिलित और हटाए गए तालिकाओं को इस तरह से आदेश दिया जाता है कि एक साथ सम्मिलित और हटाए गए तालिकाओं के माध्यम से कर्सर आपको उचित मिलान पंक्तियां देगा। और यह काम करने के लिए अपवाद। असल में आप ट्रिगर को ओरेकल में उपलब्ध प्रत्येक-पंक्ति ट्रिगर्स के बराबर में बदलते हैं और MySQL में अनिवार्य है ... लेकिन मुझे लगता है कि प्रदर्शन बड़े पैमाने पर अपडेट पर खराब होगा क्योंकि यह SQL सर्वर के मूल व्यवहार नहीं है। यह भी एक धारणा पर निर्भर करता है कि मैं वास्तव में कहीं भी दस्तावेज नहीं ढूंढ सकता हूं और इसलिए इस पर निर्भर रहने के लिए अनिच्छुक हूं। लेकिन कोड ने इस तरह से संरचित किया है कि मेरे SQL सर्वर 2008 R2 स्थापना पर ठीक से काम करने के लिए APPEARS। इस पोस्ट के अंत में लिपि तेजी से लेकिन बमरोधी समाधान और दूसरे, छद्म-ओरेकल समाधान के व्यवहार दोनों के व्यवहार को हाइलाइट करती है।

किसी को भी किसी ऐसे स्थान पर जहां मेरी धारणा दस्तावेज और गारंटी है माइक्रोसॉफ्ट द्वारा मैं एक बहुत ही आभारी पुरुष होगी करने के लिए मुझसे बात कर सकता है ...

begin try 
    drop table kpTest; 
end try 
begin catch 
end catch 
go 

create table kpTest(id int primary key, name nvarchar(10)) 
go 

begin try 
    drop trigger kpTest_ioU; 
end try 
begin catch 
end catch 
go 

create trigger kpTest_ioU on kpTest 
instead of update 
as 
begin 
    if exists 
    (
    select * 
     from inserted lost 
      left join deleted match 
       on match.id = lost.id 
    where match.id is null 
    union 
    select * 
     from deleted new 
      left join inserted match 
       on match.id = new.id 
     where match.id is null 
    ) 
     raisError('Changed primary key', 16, 1) 
    else 
    update kpTest 
     set name = i.name 
     from kpTest 
      join inserted i 
       on i.id = kpTest.id 
    ; 
end 
go 

insert into kpTest(id, name) values(0, 'zero'); 
insert into kpTest(id, name) values(1, 'one'); 
insert into kpTest(id, name) values(2, 'two'); 
insert into kpTest(id, name) values(3, 'three'); 

select * from kpTest; 

/* 
0 zero 
1 one 
2 two 
3 three 
*/ 

-- This throws an error, appropriately 
update kpTest set id = 5, name = 'FIVE' where id = 1 
go 

select * from kpTest; 

/* 
0 zero 
1 one 
2 two 
3 three 
*/ 

-- This allows the change, inappropriately 
update kpTest 
    set id = case 
       when id = 1 then 2 
       when id = 2 then 1 
       else id 
       end 
    , name = UPPER(name) 
go 

select * from kpTest 

/* 
0 ZERO 
1 TWO -- WRONG WRONG WRONG 
2 ONE -- WRONG WRONG WRONG 
3 THREE 
*/ 

-- Put it back 
update kpTest 
    set id = case 
       when id = 1 then 2 
       when id = 2 then 1 
       else id 
       end 
    , name = LOWER(name) 
go 

select * from kpTest; 

/* 
0 zero 
1 one 
2 two 
3 three 
*/ 

drop trigger kpTest_ioU 
go 

create trigger kpTest_ioU on kpTest 
instead of update 
as 
begin 
    declare newIDs cursor for select id, name from inserted; 
    declare oldIDs cursor for select id from deleted; 
    declare @thisOldID int; 
    declare @thisNewID int; 
    declare @thisNewName nvarchar(10); 
    declare @errorFound int; 
    set @errorFound = 0; 
    open newIDs; 
    open oldIDs; 
    fetch newIDs into @thisNewID, @thisNewName; 
    fetch oldIDs into @thisOldID; 
    while @@FETCH_STATUS = 0 and @errorFound = 0 
    begin 
     if @thisNewID != @thisOldID 
     begin 
      set @errorFound = 1; 
      close newIDs; 
      deallocate newIDs; 
      close oldIDs; 
      deallocate oldIDs; 
      raisError('Primary key changed', 16, 1); 
     end 
     else 
     begin 
      update kpTest 
      set name = @thisNewName 
      where id = @thisNewID 
      ; 
      fetch newIDs into @thisNewID, @thisNewName; 
      fetch oldIDs into @thisOldID; 
     end 
    end; 
    if @errorFound = 0 
    begin 
     close newIDs; 
     deallocate newIDs; 
     close oldIDs; 
     deallocate oldIDs; 
    end 
end 
go 

-- Succeeds, appropriately 
update kpTest 
    set name = UPPER(name) 
go 

select * from kpTest; 

/* 
0 ZERO 
1 ONE 
2 TWO 
3 THREE 
*/ 

-- Succeeds, appropriately 
update kpTest 
    set name = LOWER(name) 
go 

select * from kpTest; 

/* 
0 zero 
1 one 
2 two 
3 three 
*/ 


-- Fails, appropriately 
update kpTest 
    set id = case 
       when id = 1 then 2 
       when id = 2 then 1 
       else id 
       end 
go 

select * from kpTest; 

/* 
0 zero 
1 one 
2 two 
3 three 
*/ 

-- Fails, appropriately 
update kpTest 
    set id = id + 1 
go 

select * from kpTest; 

/* 
0 zero 
1 one 
2 two 
3 three 
*/ 

-- Succeeds, appropriately 
update kpTest 
    set id = id, name = UPPER(name) 
go 

select * from kpTest; 

/* 
0 ZERO 
1 ONE 
2 TWO 
3 THREE 
*/ 

drop table kpTest 
go 
+0

जाहिर है यह सिर्फ यही विचार है कि मुझे लगता है कि क्रिस केएल 200 9 में वापस आया था –

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