Trigger

如何使用 DBMS_ALERT 從 Oracle (10g R1) 觸發器實現程序間通信?

  • September 19, 2014

讓我先聲明一下,我根本不精通 Oracle,MS SQL 是的,但不是 Oracle;這就是我問這個的原因。

目前設置

  • 在 Red Hat Ent 上執行的 Oracle 10g 第 1 版。3(不支持)
  • 無法升級作業系統或數據庫
  • 不能廣泛修改數據庫(最好是只讀的)
  • 使用 Visual Studio 2013 和 .net 4.5 訪問 Oracle 的數據
  • 使用 ODAP/ODP.NET 11.2 第 5 版(由於使用 Oracle 10g R1,不能更新)
  • 使用 System.Data.Oracle 而不是 ODP.NET 類來避免文件膨脹
  • 無法使用數據更改通知,因為 10g R1 不支持它

在目前設置下,我可以輕鬆連接到數據庫、發出 sql 語句和讀/寫數據。正在編寫的應用程序將用作 Oracle 數據庫和 MongoDB 之間的一種適配器或橋樑,其中只有幾個表用於將更改複製到 MongoDB。

目前的實現使用DBMS_ALERT並將triggers事件傳遞到 .net 應用程序中,此時它將讀取數據、轉換數據並將其寫入 MongoDB。

這是正在使用的範例觸發器

CREATE OR REPLACE TRIGGER EXAMPLE.TABLENAME_ALERT
AFTER UPDATE OR INSERT OR DELETE
   ON EXAMPLE.TABLENAME
   FOR EACH ROW

BEGIN

   IF INSERTING THEN
       DBMS_ALERT.SIGNAL('DATA_INSERTED', :new.data_id);
       DBMS_OUTPUT.PUT_LINE('INSERTED');
   ELSIF UPDATING THEN
       DBMS_ALERT.SIGNAL('DATA_UPDATED', :old.data_id);
       DBMS_OUTPUT.PUT_LINE('UPDATED');
   ELSIF DELETING THEN
       DBMS_ALERT.SIGNAL('DATA_DELETED', :old.data_id);
       DBMS_OUTPUT.PUT_LINE('DELETED');
   END IF;

END;

如果我發出以下 SQL 語句:

update EXAMPLE.TABLENAME set example_column = 'test' where data_id = 1;
update EXAMPLE.TABLENAME set example_column = 'test' where data_id = 2;
update EXAMPLE.TABLENAME set example_column = 'test' where data_id = 3;

結果是在數據庫輸出中獲得 3 個單獨的DATA_UPDATED信號和 3 個單獨的行。UPDATED另一方面,如果我發布此聲明:

update EXAMPLE.TABLENAME set example_column = 'test' where data_id <= 3;

我得到的結果是 1 個DATA_UPDATED信號,其中包含更新的最後一行的 id 和UPDATED數據庫輸出中的 3 個單獨的行。我需要獲得所有 3 個信號。

我的猜測是,因為DBMS_ALERT.SIGNAL在送出事務之前不會觸發,因此只會觸發當時最新的信號,並且單個批量更新/插入/刪除被認為是 1 個事務,那麼我將收到的只是來自數據庫的 1 個信號.

有沒有一種方法可以讓每個觸發器多次呼叫DBMA_ALERT.SIGNAL以實際發送所有信號?或者也許有更好的方法來檢測外部應用程序對數據庫中數據的更改?

編輯我知道這個實現有程式碼味道,但我的雙手被束縛,我必須使用我們擁有的東西並且不能真正改變任何東西。

這可以通過這種方式完成。我不太喜歡它,因為它不是很優雅,如果發生意外的事情看起來很脆弱。

--1)Create a table in the user's schema or another new schema if you want separation.

CREATE TABLE DATA_AUDIT
( Primary_key RAW(16) default SYS_GUID() not null,
 Data_id  Number(10),
 Action   VARCHAR2(10),
 Date_created TIMESTAMP(6)DEFAULT CURRENT_TIMESTAMP);
--add foreign key constraint for Data_id to the parent Table
--add check constraint: Action in ('INSERT','UPDATE','DELETE');

--2) create a trigger on the parent table
CREATE OR REPLACE TRIGGER TGR_PARENT_TABLE
BEFORE INSERT OR UPDATE OR DELETE
ON PARENT_TABLE
FOR EACH ROW
v_action DATA_AUDIT.action%type;
BEGIN

   IF INSERTING THEN
      v_action := 'INSERT';
   ELSIF DELETING THEN
       v_action := 'DELETE';
   ELSIF UPDATING THEN
       v_action := 'UPDATE';
   ELSE
       --raise an error for an unexpected event and log it
       RAISE;
   END IF;
      Insert into DATA_AUDIT(Primary_key,Data_id, Action,Date_created) VALUES
      (SYS_GUID(), :old.Data_id, v_action,CURRENT_TIMESTAMP);
END;

--3) create a trigger on DATA_AUDIT
CREATE OR REPLACE TRIGGER TGR_DATA_AUDIT
BEFORE INSERT
ON DATA_AUDIT
FOR EACH ROW

BEGIN

DBMS_ALERT.SIGNAL(:old.action, :old.data_id);
END;

引用自:https://dba.stackexchange.com/questions/77100