【问题标题】:Oracle equivalent for SQL Server INSERTED and DELETED tablesSQL Server INSERTED 和 DELETED 表的 Oracle 等效项
【发布时间】:2014-10-24 17:53:49
【问题描述】:

我正在将 SQL Server 数据库迁移到 Oracle,我必须在其中转换 SQL Server 过程,该过程在 SQL Server 中使用称为 INSERTEDDELETED 的特殊表。

据我了解,这些表包含上次插入/删除记录的数据的副本。 (在此处找到 msdn 文章:http://msdn.microsoft.com/en-us/library/ms191300.aspx

Oracle 中是否有任何类似的表来实现这一点..?请指教。

更新:

感谢您的回答和cmets,我想我需要再解释一下情况。这是了解真实情况的完整故事;

  • 数据库包含表和影子表(影子有一个附加列)。
  • 更新表时,应在相关影子表中记录相同的更改以及一些附加数据。
  • 为此,他们为每个表设置了触发器(这些触发器将数据复制到相关影子表)。
  • 上述过程为每个表动态生成这些触发器。
  • 现在真正的问题是我不了解列,因为触发器是为每个表动态生成的。
  • 基本上我无法获得像 APC 提到的 NEW.col_1 或 OLD.col_1 这样的值。我可以吗?

否则我必须使用前缀手动编写所有这些触发器:NEW 和:OLD,而不是尝试动态生成它们。

我正在使用 Oracle 11g

【问题讨论】:

  • 你了解语句级触发器和行级触发器的区别吗? Oracle 中通常不需要inserteddeleted“表”。使用行级触发器并使用newold 记录直接操作列值。
  • 不知道为什么人们认为这是一个 DBA 问题。触发器肯定在程序员的工作范围内。

标签: sql sql-server oracle triggers


【解决方案1】:

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

【讨论】:

  • 感谢 APC,我按照您的指导找到了解决方案。
【解决方案2】:

Sql Server 触发器和Oracle 触发器有很多不同之处。在 Oracle 中,您可以声明语句级或行级触发器。 Sql Server 只有语句级。在 Oracle 中,您可以在触发器之前或触发器之后声明。 Sql Server 只有 after 触发器。

如果您打算使用 Oracle,尽管更高版本有 compound trigger,但请习惯使用行级触发器。在那里你有 :old 和 :new 的伪行名称,有点像 Deleted 和 Inserted,除了它只是一行数据。这就像处于游标循环中,您可以在 Sql Server 中执行某些操作,但是游标在 Sql Server 中的性能很差,开发人员竭尽全力避免它们。它们在 Oracle 中很常用。

一般的经验法则是:如果您需要检查数据并可能在数据进入表之前对其进行更改,请使用“之前”触发器。如果要执行审计或日志记录过程,请使用“after”触发器。

我上面链接的页面提供了很多技术细节,但在提供可用示例方面绝对是粗暴的。为此,只需 google 一下“oracle trigger tutorial”,您就会得到很多方便、易于学习的示例。

【讨论】:

  • SQL Server 确实有instead of 触发器,我假设它们的作用与之前的触发器相似。
  • Oracle 和 Sql Server 在视图上都有 instead of 触发器。它们的工作方式与表触发器相同——Oracle 是“针对每一行”(:old, :new),而 Sql Server 是“针对每个语句”(删除、插入)。我大力提倡尽可能频繁地使用视图。 Sql Server 的好处之一是“覆盖”视图提供了在基础表上实现“之前”触发器的能力。
  • 您可以在 SQL Server 中的表和视图上创建而不是触发器。
【解决方案3】:

感谢您的回答和 cmets。这是我的问题的完整解决方案。如果有人遇到确切的问题,这将有所帮助。

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;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-15
    • 1970-01-01
    • 2012-09-04
    • 2014-09-29
    • 1970-01-01
    • 1970-01-01
    • 2011-09-04
    相关资源
    最近更新 更多