else
. के साथ आपकी तत्काल समस्या हमेशा कॉल किया जा रहा है क्योंकि आप अपने इंडेक्स वेरिएबल r
. का उपयोग कर रहे हैं सीधे, प्रासंगिक कॉलम नाम देखने के बजाय:
for r in v_tab_col_nt.first..v_tab_col_nt.last
loop
if updating(v_tab_col_nt(r)) then
insert into data_table values(1,'i am updating '||v_tab_col_nt(r));
else
insert into data_table values(2,'i am inserting '||v_tab_col_nt(r));
end if;
end loop;
आप भी केवल एक id
दिखा रहे हैं आपकी तालिका निर्माण में कॉलम, इसलिए जब r
है 2
, यह हमेशा कहेगा कि यह सम्मिलित कर रहा है name
, कभी अद्यतन नहीं। इससे भी महत्वपूर्ण बात यह है कि यदि आपके पास नाम
है तो कॉलम और केवल एक दिए गए id
. के लिए इसे अपडेट कर रहे थे , यह कोड id
. दिखाएगा डालने के रूप में जब यह नहीं बदला था। आपको इन्सर्ट/अपडेट को अलग-अलग ब्लॉकों में विभाजित करने की आवश्यकता है:
if updating then
for r in v_tab_col_nt.first..v_tab_col_nt.last loop
if updating(v_tab_col_nt(r)) then
insert into data_table values(1,'i am updating '||v_tab_col_nt(r));
end if;
end loop;
else /* inserting */
for r in v_tab_col_nt.first..v_tab_col_nt.last loop
insert into data_table values(2,'i am inserting '||v_tab_col_nt(r));
end loop;
end if;
यह अभी भी कहेगा कि यह name
सम्मिलित कर रहा है भले ही कॉलम मौजूद न हो, लेकिन मुझे लगता है कि यह एक गलती है, और मुझे लगता है कि आप user_tab_columns
से नामों की सूची को पॉप्युलेट करने का प्रयास कर रहे होंगे। वैसे भी यदि आप वास्तव में इसे गतिशील बनाने का प्रयास करना चाहते हैं।
मैं दूसरों से (कम से कम कुछ) सहमत हूं कि आप शायद एक ऑडिट टेबल के साथ बेहतर होंगे जो अलग-अलग कॉलम की बजाय पूरी पंक्ति की एक प्रति लेता है। आपकी आपत्ति व्यक्तिगत रूप से सूचीबद्ध करने की जटिलता प्रतीत होती है कि कौन से कॉलम बदल गए हैं। जब आपको कॉलम-दर-कॉलम डेटा की आवश्यकता होती है, तब भी आप ऑडिट तालिका को अनपिवो करके, थोड़े से काम के साथ, यह जानकारी प्राप्त कर सकते हैं। उदाहरण के लिए:
create table temp12(id number, col1 number, col2 number, col3 number);
create table temp12_audit(id number, col1 number, col2 number, col3 number,
action char(1), when timestamp);
create or replace trigger temp12_trig
before update or insert on temp12
for each row
declare
l_action char(1);
begin
if inserting then
l_action := 'I';
else
l_action := 'U';
end if;
insert into temp12_audit(id, col1, col2, col3, action, when)
values (:new.id, :new.col1, :new.col2, :new.col3, l_action, systimestamp);
end;
/
insert into temp12(id, col1, col2, col3) values (123, 1, 2, 3);
insert into temp12(id, col1, col2, col3) values (456, 4, 5, 6);
update temp12 set col1 = 9, col2 = 8 where id = 123;
update temp12 set col1 = 7, col3 = 9 where id = 456;
update temp12 set col3 = 7 where id = 123;
select * from temp12_audit order by when;
ID COL1 COL2 COL3 A WHEN
---------- ---------- ---------- ---------- - -------------------------
123 1 2 3 I 29/06/2012 15:07:47.349
456 4 5 6 I 29/06/2012 15:07:47.357
123 9 8 3 U 29/06/2012 15:07:47.366
456 7 5 9 U 29/06/2012 15:07:47.369
123 9 8 7 U 29/06/2012 15:07:47.371
तो आपके पास की गई प्रत्येक कार्रवाई के लिए एक ऑडिट पंक्ति, दो प्रविष्टियां और तीन अपडेट हैं। लेकिन आप बदले हुए प्रत्येक कॉलम के लिए अलग डेटा देखना चाहते हैं।
select distinct id, when,
case
when action = 'I' then 'Record inserted'
when prev_value is null and value is not null
then col || ' set to ' || value
when prev_value is not null and value is null
then col || ' set to null'
else col || ' changed from ' || prev_value || ' to ' || value
end as change
from (
select *
from (
select id,
col1, lag(col1) over (partition by id order by when) as prev_col1,
col2, lag(col2) over (partition by id order by when) as prev_col2,
col3, lag(col3) over (partition by id order by when) as prev_col3,
action, when
from temp12_audit
)
unpivot ((value, prev_value) for col in (
(col1, prev_col1) as 'col1',
(col2, prev_col2) as 'col2',
(col3, prev_col3) as 'col3')
)
)
where value != prev_value
or (value is null and prev_value is not null)
or (value is not null and prev_value is null)
order by when, id;
ID WHEN CHANGE
---------- ------------------------- -------------------------
123 29/06/2012 15:07:47.349 Record inserted
456 29/06/2012 15:07:47.357 Record inserted
123 29/06/2012 15:07:47.366 col1 changed from 1 to 9
123 29/06/2012 15:07:47.366 col2 changed from 2 to 8
456 29/06/2012 15:07:47.369 col1 changed from 4 to 7
456 29/06/2012 15:07:47.369 col3 changed from 6 to 9
123 29/06/2012 15:07:47.371 col3 changed from 3 to 7
पांच ऑडिट रिकॉर्ड सात अपडेट में बदल गए हैं; तीन अपडेट स्टेटमेंट संशोधित पांच कॉलम दिखाते हैं। यदि आप इसका बहुत अधिक उपयोग कर रहे हैं, तो आप इसे एक दृश्य बनाने पर विचार कर सकते हैं।
तो चलिए इसे थोड़ा सा तोड़ते हैं। मूल यह आंतरिक चयन है, जो लैग ()
उस id
. के पिछले ऑडिट रिकॉर्ड से पंक्ति का पिछला मान प्राप्त करने के लिए :
select id,
col1, lag(col1) over (partition by id order by when) as prev_col1,
col2, lag(col2) over (partition by id order by when) as prev_col2,
col3, lag(col3) over (partition by id order by when) as prev_col3,
action, when
from temp12_audit
यह हमें एक अस्थायी दृश्य देता है जिसमें सभी ऑडिट टेबल कॉलम और लैग कॉलम होता है जिसका उपयोग तब unpivot()
ऑपरेशन, जिसका आप उपयोग कर सकते हैं क्योंकि आपने प्रश्न को 11g के रूप में टैग किया है:
select *
from (
...
)
unpivot ((value, prev_value) for col in (
(col1, prev_col1) as 'col1',
(col2, prev_col2) as 'col2',
(col3, prev_col3) as 'col3')
)
अब हमारे पास एक अस्थायी दृश्य है जिसमें id, क्रिया, कब, col, value, prev_value
है स्तंभ; इस मामले में क्योंकि मेरे पास केवल तीन कॉलम हैं, जिसमें ऑडिट टेबल में पंक्तियों की संख्या तीन गुना है। अंत में बाहरी चयन फ़िल्टर जो देखने के लिए केवल वे पंक्तियाँ शामिल करते हैं जहाँ मान बदल गया है, अर्थात जहाँ value !=prev_value
(नल के लिए अनुमति)।
select
...
from (
...
)
where value != prev_value
or (value is null and prev_value is not null)
or (value is not null and prev_value is null)
मैं केस
का उपयोग कर रहा हूं बस कुछ प्रिंट करने के लिए, लेकिन निश्चित रूप से आप डेटा के साथ जो चाहें कर सकते हैं। विशिष्ट
की आवश्यकता है क्योंकि सम्मिलित करें
अंकेक्षण तालिका में प्रविष्टियां भी अविभाजित दृश्य में तीन पंक्तियों में परिवर्तित हो जाती हैं, और मैं अपने पहले मामले
से तीनों के लिए एक ही पाठ दिखा रहा हूं खंड।