2014-08-31 91 views
2

我正在將SQL Server數據庫遷移到Oracle,在那裏我必須轉換使用SQL Server中的特殊表INSERTEDDELETED的SQL Server過程。Oracle等價於SQL Server INSERTED和DELETED表

根據我的理解,這些表保存了上次插入/刪除記錄的數據。 (在這裏找到msdn的文章:http://msdn.microsoft.com/en-us/library/ms191300.aspx

Oracle中有沒有類似的表來實現這個..?請指教。

UPDATE:

謝謝您的回答和評論,我想我需要說明的情況更多一些。以下是瞭解真實場景的完整故事;

  • 數據庫包含表和陰影表(陰影有一個額外的列)。
  • 更新表格時應該在相關的影子表格中記錄相同的更改並附加一些數據。
  • 爲此,他們對每個表都有觸發器(這些觸發器將數據複製到相關的影子表中)。
  • 上述過程爲每個表動態地生成這些觸發器。
  • 現在真正的問題是我沒有關於列的知識,因爲觸發器是爲每個表動態生成的。
  • 基本上我不能像APC提到的那樣獲得像NEW.col_1或OLD.col_1這樣的值。我可以嗎。?

否則我必須使用前綴手動編寫所有這些觸發器:NEW和:OLD,而不是試圖動態生成它們。

我正在使用Oracle 11g

+1

您是否理解語句級觸發器和行級觸發器之間的區別?您通常不需要Oracle中的「插入」或「刪除」表。使用行級別觸發器並直接使用'new'和'old'記錄操縱列值。 – 2014-08-31 08:03:19

+0

不確定爲什麼人們認爲這是一個DBA問題。觸發器絕對是程序員的職位。 – APC 2014-08-31 08:11:17

回答

0

感謝您的答案和評論。這裏是我的問題的完整解決方案。如果有人遇到確切的問題,這將有所幫助。

create or replace PROCEDURE CreateTrackingTriggers 
(
-- take the target table and shadow user as agruments 
v_TableName IN NVARCHAR2 DEFAULT NULL, 
v_ShadowUser IN NVARCHAR2 DEFAULT 'SHADOW_USER' 
) 
AUTHID CURRENT_USER -- grant permission to create triggers 
AS 
v_TriggerName NVARCHAR2(500); 
v_ColList NVARCHAR2(2000); 
v_ColList_shadow NVARCHAR2(2000); 
v_SQLCommand VARCHAR2(4000); 
v_ColName NVARCHAR2(500); 
v_ColSize NUMBER(10,0); 
v_Prefix NVARCHAR2(500); 
v_count NUMBER(1,0); 

BEGIN 

DECLARE 
    -- define a cursor to get the columns of the target table. order by COLUMN_ID is important 
    CURSOR Cols 
    IS SELECT COLUMN_NAME , CHAR_COL_DECL_LENGTH FROM USER_TAB_COLS 
    WHERE TABLE_NAME = upper(v_TableName) order by COLUMN_ID; 
    -- define a cursor to get the columns of the target shadow table order by COLUMN_ID is important 
    CURSOR Shadow_Cols 
    IS SELECT COLUMN_NAME , CHAR_COL_DECL_LENGTH FROM ALL_TAB_COLS 
    WHERE TABLE_NAME = upper(v_TableName) and upper(owner)=upper(v_ShadowUser) order by COLUMN_ID; 

BEGIN 
    -- generate the trigger name for target table 
    v_TriggerName := 'TRG_' || upper(v_TableName) || '_Track' ; 

    -- check v_count , determine whether shdow table exist if not handle it 
    select count(*) into v_count from all_tables where table_name = upper(v_TableName) and owner = upper(v_ShadowUser); 

-- iterate the cursor. generating column names prefixing ':new.' 

    OPEN Cols; 
    FETCH Cols INTO v_ColName,v_ColSize; 
WHILE Cols%FOUND 
    LOOP 

    BEGIN 
      IF v_ColList IS NULL THEN 
      v_ColList := ':new.'||v_ColName ; 
      ELSE 
      v_ColList := v_ColList || ',' || ':new.'||v_ColName; 
      END IF; 

     FETCH Cols INTO v_ColName,v_ColSize; 
    END; 
    END LOOP; 

    CLOSE Cols; 

    -- iterate the cursor. get the shadow table columns 

    OPEN Shadow_Cols; 
    FETCH Shadow_Cols INTO v_ColName,v_ColSize; 

WHILE Shadow_Cols%FOUND 
    LOOP 

    BEGIN 

      IF v_ColList_shadow IS NULL THEN 
      v_ColList_shadow := v_ColName; 
      ELSE 
      v_ColList_shadow := v_ColList_shadow || ',' || v_ColName; 
      END IF; 

     FETCH Shadow_Cols INTO v_ColName,v_ColSize; 
    END; 
    END LOOP; 

    CLOSE Shadow_Cols; 

-- create trigger command. This will generate the trigger that dupilicates target table's data into shdow table 
v_SQLCommand := 'CREATE or REPLACE TRIGGER '||v_TriggerName||' 
AFTER INSERT OR UPDATE OR DELETE ON '||upper(v_TableName)||' 
REFERENCING OLD AS old NEW AS new 
FOR EACH ROW 

DECLARE  
    ErrorCode NUMBER(19,0); 
BEGIN 

-- v_ColList_shadow : shdow table column list 
-- v_ColList : target table column list with :new prefixed 
INSERT INTO '|| v_ShadowUser ||'.'||upper(v_TableName)||'('||v_ColList_shadow||') values ('||v_ColList||'); 
EXCEPTION 
    WHEN OTHERS THEN ErrorCode := SQLCODE; 
END;'; 

EXECUTE IMMEDIATE v_SQLCommand; 
END; 
END; 
6

Oracle觸發器使用僞記錄而不是特殊表。也就是說,我們可以訪問各個列的值,而不是表格。

我們通過使用前綴:NEW:OLD來區分受影響的表中的僞記錄與(其他)表中的記錄。 Oracle允許我們爲這些聲明自己的名字,但放棄標準確實沒有什麼好的理由。

我們可以訪問哪些列值?

Action  :OLD    :NEW 
------  ----    ---- 
INSERTING n/a     Inserted value 
UPDATING  Superseded value Amended value 
DELETING  Deleted value  n/a 

你會看到:OLD是一樣的MSSQL表DELETED:NEW是一樣的表INSERTED

因此,觸發當某個列被更新業務規則檢查:

create or replace trigger t23_bus_check_trg 
    before update on t23 
    for each row 
begin 
    if :NEW.col_1 != :OLD.col_1 then 
     check_this(:NEW.col_1 , :OLD.col_1); 
    end if; 
end t23_bus_check_trg;  

關於PL/SQL參考文獻中的記錄有一整章。 Find out more

+0

感謝APC,我根據您的指導達成了解決方案。 – LittleBit 2014-09-03 05:19:25

3

Sql Server觸發器和Oracle觸發器之間有許多區別。在Oracle中,您可以聲明語句級別或行級別觸發器。 Sql Server只有語句級別。在Oracle中,您可以在觸發器之前或觸發器之後聲明。Sql Server只有在觸發器之後。

如果你要與Oracle合作,雖然後來的版本有compound trigger,習慣與行級觸發器工作。在那裏,僞行指定爲:old和:new,有點像Deleted和Inserted,除了它只是一行數據。這就像在一個遊標循環是,有些東西你可以在SQL Server做,但光標在SQL Server,以便表現不佳,開發商竭盡全力避免。它們通常在Oracle中使用。

一般的經驗法則是:如果你需要檢查的數據,並可能改變它,它去表之前,使用一個「前」觸發。如果您想執行審計或記錄程序,請使用「after」觸發器。

我鏈接到網頁上面給出了很多的技術細節,但它是在給可用的例子絕對殘暴。爲此,只需谷歌「甲骨文觸發教程」,你應該得到很多方便,容易學習的例子。

+1

SQL Server確實具有'而不是'觸發器,我認爲它在觸發器之前扮演着類似的角色。 – 2014-08-31 10:24:17

+0

Oracle和Sql Server都在視圖上使用'而不是'觸發器。而他們的工作爲表觸發同樣的方式 - 甲骨文是「每一行」(:舊,新)和SQL Server是「爲每個語句」(刪除,插入)。我很樂意使用視圖,因爲這樣做是明智的。 Sql Server的一個優點是「覆蓋」視圖提供了在底層表上實現「before」觸發器的能力。 – TommCatt 2014-09-03 05:12:36

+1

您可以在SQL Server的表和視圖中創建而不是觸發器。 – 2014-09-03 05:15:13

相關問題