从SQL SERVER向ORACLE的迁移方案[转] PartII

 

<六> 触发器
    <1>、SQL SERVER端语法说明

    1、语法:

           CREATE TRIGGER [owner.]trigger_name

           ON [owner.]table_name

           FOR { INSERT, UPDATE, DELETE }

             [WITH ENCRYPTION]

           AS

             sql_statement [...n]

       或者使用IF UPDATE子句:

           CREATE TRIGGER [owner.]trigger_name

           ON [owner.]table_name

           FOR { INSERT, UPDATE }

             [WITH ENCRYPTION]

           AS

             IF UPDATE (column_name)

             [{AND | OR} UPDATE (column_name)…]

             sql_statement [ ...n]

 

     2、示例:

        IF EXISTS (SELECT 1 FROM sysobjects

                   WHERE name = 'reminder' AND type = 'TR')

          DROP TRIGGER reminder

        GO

 

        CREATE TRIGGER employee_insupd

               ON employee

              FOR INSERT, UPDATE

        AS

        /* Get the range of level for this job type from the jobs table. */

        DECLARE @min_lvl tinyint,

                    @max_lvl tinyint,

                    @emp_lvl tinyint,

                    @job_id  smallint

         SELECT @min_lvl = min_lvl,

                    @max_lvl = max_lvl,

                    @emp_lvl = i.job_lvl,

                    @job_id  = i.job_id

           FROM employee e, jobs j, inserted i

          WHERE e.emp_id = i.emp_id AND i.job = j.job_id

        IF (@job_id = 1) and (@emp_lvl <> 10)

        BEGIN

               RAISERROR ('Job id 1 expects the default level of 10.', 16, 1)

              ROLLBACK TRANSACTION

        END

        ELSE

        IF NOT (@emp_lvl BETWEEN @min_lvl AND @max_lvl)

        BEGIN

             RAISERROR ('The level for job_id:%d should be between %d and %d.',

                         16, 1, @job_id, @min_lvl, @max_lvl)

             ROLLBACK TRANSACTION

        END

        GO

 

    <2>、ORACLE端语法说明

    1、语法:

           CREATE [OR REPLACE] TRIGGER trigger_name

           { BEFORE | AFTER } triggering_event ON  table_name

           [ FOR EACH ROW ]

           [ WHEN trigger_condition ]

           trigger_body ;

    2、使用说明与示例:

       (1)、上语法中,trigger_event 是对应于DML的三条语句INSERT、UPDATE、

             DELETE;table_name是与触发器相关的表名称;FOR EACH ROW是可选

             子句,当使用时,对每条相应行将引起触发器触发;condition是可选的

             ORACLE BOOLEAN条件,当条件为真时触发器触发;trigger_body是触发

             器触发时执行的PL/SQL块。

 

       (2)、ORACLE触发器有以下两类:

             1> 语句级(Statement-level)触发器,在CREATE TRIGGER语句中不

                包含FOR EACH ROW子句。语句级触发器对于触发事件只能触发一次,

                而且不能访问受触发器影响的每一行的列值。一般用语句级触发器处理

                有关引起触发器触发的SQL语句的信息——例如,由谁来执行和什么时

                间执行。

             2> 行级(Row-level)触发器,在CREATE TRIGGER语句中

                包含FOR EACH ROW子句。行级触发器可对受触发器影响的每一行触

                发,并且能够访问原列值和通过SQL语句处理的新列值。行级触发器的

                典型应用是当需要知道行的列值时,执行一条事务规则。

 

        (3)在触发器体内,行级触发器可以引用触发器触发时已存在的行的列值,这些

             值倚赖于引起触发器触发的SQL语句。

             1> 对于INSERT语句,要被插入的数值包含在new.column_name,这里的

                column_name是表中的一列。

             2> 对于UPDATE语句,列的原值包含在old.column_name中,数据列的新

                值在new.column_name中。

             3> 对于DELETE语句,将要删除的行的列值放在old.column_name中。

触发语句
 :old
 :new
 
INSERT
 无定义——所有字段都是NULL
 当该语句完成时将要插入的数值
 
UPDATE
 在更新以前的该行的原始取值
 当该语句完成时将要更新的新值
 
DELETE
 在删除行以前的该行的原始取值
 未定义——所有字段都是NULL
 

             4> 在触发器主体中,在new和old前面的“:”是必需的。而在触发器的

                WHEN子句中,:new和:old记录也可以在WHEN子句的condition内部

                引用,但是不需要使用冒号。例如,下面CheckCredits触发器的主体仅

                当学生的当前成绩超过20时才会被执行:

                CREATE OR REPLACE TRIGGER CheckCredits

                   BEFORE INSERT OR UPDATE OF current_credits ON students

                   FOR EACH ROW

                   WHEN (new.current_credits > 20)

                BEGIN

                   /*Trigger body goes here. */

                END ;

                但CheckCredits也可以按下面方式改写:

                CREATE OR REPLACE TRIGGER CheckCredits

                   BEFORE INSERT OR UPDATE OF current_credits ON students

                   FOR EACH ROW

                BEGIN

                   IF  :new.current_credits > 20  THEN

                      /*Trigger body goes here. */

                   END IF ;

                END ;

                注意,WHEN子句仅能用于行级触发器,如果使用了它,那么触发器主体

                仅仅对那些满足WHEN子句指定的条件的行进行处理。

 

        (4)触发器的主体是一个PL/SQL块,在PL/SQL块中可以使用的所有语句在触

             发器主体中都是合法的,但是要受到下面的限制:

             1> 触发器不能使用事务控制语句,包括COMMIT、ROLLBACK或

                SAVEPOINT。ORACLE保持这种限制的原因是:如果触发器遇到错误时,

                由触发器导致的所有数据库变换均能被回滚(roll back)取消;但如果

                触发器确认(commit)了对数据库进行的部分变换,ORACLE就不能完全

                回滚(roll back)整个事务。

             2> 在触发器主体中调用到的存储过程的实现语句里也不能使用事务控制语

                句。

             3> 触发器主体不能声明任何LONG或LONG RAW变量。而且,:new和:old

                不能指向定义触发器的表中的LONG或LONG RAW列。

             4> 当声明触发器的表中有外键约束时,如果将定义触发器的表和需要作为

                DELETE CASCADE参考完整性限制的结果进行更新的表称为变化表,

                将外键相关联的表称为限制表,则在此触发器主体中的SQL语句不允许

                读取或修改触发语句的任何变化表,也不允许读取或修改限制表中的主

                键、唯一值列或外键列。

 

        (5)以下是建立一个事前插入触发器的示例:

             CREATE OR REPLACE TRIGGER Credit_Charge_Log_Ins_Before

                BEFORE insert ON Credit_Charge_Log

                FOR EACH ROW

             DECLARE

                Total_for_past_3days     number ;

             BEGIN

                -- Check the credit charges for the past 3 days.

                -- If they total more than $1000.00, log this entry

                -- int the Credit_Charge_Attempt_Log for further handling.

                select sum ( amount screen.width/2)this.style.width=screen.width/2;" border=0> into total_for_past_3days

                  from Credit_Charge_Log

                 where Card_Number = :new.Card_Number

                   and Transaction_Date >= sysdate – 3;

                IF total_for_past_3days > 1000.00 THEN

                   insert into credit_Charge_Attemp_Log

                     (Card_Number, Amount, Vendor_ID, Transaction_Date)

                   values

                     (:new.Card_Number, :new.Amount,

                      :new.Vendor_ID, :new.Transaction_Date);

                END IF ;

             END ;

 

<3>、从SQL SERVER向ORACLE的迁移方案

 

         1、通过比较上面SQL语法的不同并考虑现有SQL SERVER的实际编程风格,在从

            T-SQL向PL/SQL迁移时,要遵守下面规则:

            1> 在CREATE TRIGGER定义中采用AFTER关键字,即调整为事后触发器。

            2> 在CREATE TRIGGER定义中采用FOR EACH ROW关键字,即调整为行级触发

               器。

            3> 将触发器主体中的“inserted”调整为“:new”,将“deleted”调整为“:old”。

            4> 在触发器主体中禁用CURSOR操作:new与:old。

            5> 在触发器主体中禁用COMMIT、ROLLBACK、SAVEPOINT等事务控制语句。

 

         2、用触发器解决ID列向SEQUENCE迁移的问题:

            下面的GenerateStudentID触发器使用了:new。这是一个before INSERT触

            发器,其目的是使用student_sequence序列所产生的数值填写

            students表的ID字段。

            例:

            CREATE OR REPLACE TRIGGER GenerateStudentID

               BEFORE INSERT ON students

               FOR EACH ROW

            BEGIN

               SELECT student_sequence.nextval

                 INTO :new.ID

                 FROM dual;

            END;

            在上面的触发器主体中,GenerateStudentID实际上修改了:new.ID的值。这

            是:new最有用的一个特性——当该语句真正被执行时,:new中的存储内容就

            将被使用。有了这个触发器,我们就可以使用下面这样的INSERT语句,而不

            会产生错误:

               INSERT INTO students (first_name, last_name)

               VALUES (‘LUO’, ‘TAO’) ;

            尽管我们没有为主键列ID(这是必需的)指定取值,触发器将会提供所需要

            的取值。事实上,如果我们为ID指定了一个取值,它也将会被忽略,因为触

            发器修改了它。如果我们使用下面的语句:

               INSERT INTO students (ID, first_name, last_name)

               VALUES (-789, ‘LUO’, ‘TAO’) ;

            其处理结果还是相同的。无论在哪种情况下,student_sequence.nextval都

            将用作ID列值。

 

            由此讨论,可以采用这种方法处理SQL SERVER中ID列向ORACLE的SEQUENCE

            转换的问题。

 

            另外,由于上面的原因,我们不能在after行级触发器中修改 :new,因为该

            语句已经被处理了。通常,:new仅仅在before行级触发器中被修改,而:old

            永远不会被修改,仅仅可以从它读出数据。

 

            此外,:new和:old记录仅仅在行级触发器内部是有效的。如果试图要从语句

            级触发器进行引用,将会得到一个编译错误。因为语句级触发器只执行一次

            ——尽管语句要处理许多行——所以:new和:old是没有意义的,因为怎么确

转载于:https://www.cnblogs.com/yeskele/archive/2006/08/08/470725.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值