2014-08-31 103 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