TRIGGER语法和创建一个限制IP登陆的triiger

编写触发器时,需要注意以下几点:

1.触发器不接受参数。

2.在一个表上的触发器越多,对在该表上的DML操作的性能影响就越大。

3.在触发器的执行部分只能用DML语句(SELECTINSERTUPDATEDELETE),不能使用DDL语句(CREATEALTERDROP)。

4.触发器中不能包含事务控制语句(COMMITROLLBACKSAVEPOINT)。因为触发器是触发语句的一部分,触发语句被提交、回退时,触发器也被提交、回退了。

(虽然创建触发器的pl/sqlcommit时触发器也可以正常创建不会报错,但是对表执行dml操作时会报错ORA-04092: COMMIT 不能在触发器中,导致无法对表执行dml操作)


5.在触发器主体中调用的任何过程、函数,都不能使用事务控制语句。

6.在行触发器的PL/SQL块中可以使用相关名称参照当前的新、旧列值,默认的相关名称分别为OLDNEW。触发器的PL/SQL块中应用相关名称时,必须在它们之前加冒号(:)

7.无法对sys用户的对象执行触发器,也就是说不能对sys用户的表创建触发器

 

 

create [or replace] TRIGGER 触发器名

触发时间

触发事件 

on 表名 

[for each row] 

[WHEN condition]

begin 

 pl/sql语句 

end

 

 

CREATE [OR REPLACE] TRIGGER trigger_name

{BEFORE | AFTER }

{INSERT | DELETE | UPDATE [OF column [,column,...]]}

ON table_name

 [FOR EACH ROW ]

[WHEN condition]

begin 

 pl/sql语句 

end

--触发时间:指明触发器何时执行,before:表示在数据库动作之前触发器执行;after:表示在数据库动作之后触发器执行。

--触发事件:指明哪些数据库动作会触发此触发器:insert:数据库插入会触发此触发器;update:数据库修改会触发此触发器;delete:数据库删除会触发此触发器。

--UPDATE [column_1,column_2,,column_x]:当触发事件是UPDATE时,如果修改了column_x列,则取值为TRUE,否则为FALSE。其中column_x是可选的。

--FOR EACH ROW选项说明触发器为行触发器。对表的每一行触发器执行一次。如果没有这一选项则只对整个表执行一次。

--WHEN 子句说明触发约束条件

 

 

以上都是表级触发器,我们做的登陆监控trigger是事件触发器所以不需要on表名,on database就可以了

CREATE [OR REPLACE] TRIGGER 触发器名

{BEFORE|AFTER }

{DDL事件1 [DDL事件2...]| 数据库事件1 [数据库事件2...]}

ON {DATABASE| [模式名.]SCHEMA }

[WHEN (条件)]

DECLARE

声明部分

BEGIN

主体部分

END;

其中:DATABASE表示创建数据库级触发器,数据库级要给出数据库事件;SCHEMA表示创建模式级触发器,模式级要给出模式事件或DDL事件。

 

 

 

 

 

1: 建立一个触发器, 当职工表 emp 表被删除一条记录时,把被删除记录写到职工表删除日志表中去。

CREATE TABLE emp_his AS SELECT * FROM EMP WHERE 1=2;

CREATE OR REPLACE TRIGGER tr_del_emp

   BEFORE DELETE --指定触发时机为删除操作前触发

   ON EMP

   FOR EACH ROW   --说明创建的是行级触发器

BEGIN

   --将修改前数据插入到日志记录表 del_emp ,以供监督使用。

   INSERT INTO emp_his(deptno , empno, ename , job ,mgr , sal , comm , hiredate ) VALUES( :old.deptno, :old.empno, :old.ename , :old.job,:old.mgr, :old.sal, :old.comm, :old.hiredate );

END;

当然表级触发器也可以declare的,但是必须放于for each row后面,begin的前面

CREATE OR REPLACE TRIGGER tr_del_emp

   BEFORE DELETE

   ON EMP

   FOR EACH ROW

   declare vv number:=2;

BEGIN

   INSERT INTO emp_his(deptno , empno, ename , job ,mgr , sal , comm , hiredate ) VALUES( :old.deptno,vv, :old.ename , :old.job,:old.mgr, :old.sal, :old.comm, :old.hiredate );

END;

 

 

 

2:限制对Departments表修改(包括INSERT,DELETE,UPDATE)的时间范围,即不允许在非工作时间修改departments表。

CREATE OR REPLACE TRIGGER tr_dept_time

BEFORE INSERT OR DELETE OR UPDATE

ON departments

BEGIN

 IF (TO_CHAR(sysdate,'DAY') IN ('星期六', '星期日')) OR (TO_CHAR(sysdate, 'HH24:MI') NOT BETWEEN '08:30' AND '18:00')

 THEN RAISE_APPLICATION_ERROR(-20001, '不是上班时间,不能修改departments');

 END IF;

END;

 

3限定只对部门号为80的记录进行行触发器操作。

CREATE OR REPLACE TRIGGER tr_emp_sal_comm

BEFORE UPDATE OF salary, commission_pct

       OR DELETE

ON HR.employees

FOR EACH ROW

WHEN (old.department_id = 80)

BEGIN

 CASE

     WHEN UPDATING ('salary') THEN

        IF :NEW.salary < :old.salary THEN

           RAISE_APPLICATION_ERROR(-20001, '部门80的人员的工资不能降');

        END IF;

     WHEN UPDATING ('commission_pct') THEN

        IF :NEW.commission_pct < :old.commission_pct THEN

           RAISE_APPLICATION_ERROR(-20002, '部门80的人员的奖金不能降');

        END IF;

     WHEN DELETING THEN

          RAISE_APPLICATION_ERROR(-20003, '不能删除部门80的人员记录');

     END CASE;

END;

 

4利用行触发器实现级联更新。在修改了主表regions中的region_id之后(AFTER),级联的、自动的更新子表countries表中原来在该地区的国家的region_id

CREATE OR REPLACE TRIGGER tr_reg_cou

AFTER update OF region_id

ON regions

FOR EACH ROW

BEGIN

 DBMS_OUTPUT.PUT_LINE('旧的region_id值是'||:old.region_id

                  ||'、新的region_id值是'||:new.region_id);

 UPDATE countries SET region_id = :new.region_id

 WHERE region_id = :old.region_id;

END;

 

 

 

 

 

创建trigger限制不在列表中的IP不能登陆DB

先建立一个表,存放IP

CREATE   TABLE DBA_CONTROL_RULE

(

  RULE_ID        NUMBER(8)     NOT NULL PRIMARY KEY,

  IP_ADDRESS     VARCHAR2(15 BYTE)

)

 

 

建立一个表,存放登陆记录

CREATE TABLE DBA_ACCESS_LOG

(

  SESSION_ID     NUMBER(10),

  IP_ADDRESS     VARCHAR2(15 BYTE),

  DB_USER_NAME   VARCHAR2(20 BYTE),

  OS_USER        VARCHAR2(50 BYTE),

  MACHINE_NAME   VARCHAR2(50 BYTE),

  TERMINAL_NAME  VARCHAR2(50 BYTE),

  PROGRAM_NAME   VARCHAR2(50 BYTE),

  ACTION_DATE    DATE

)

 

 

建立限制登陆的触发器

CREATE OR REPLACE TRIGGER SYS.DBA_LOGON_CHECK

AFTER LOGON ON DATABASE

declare

v_session_id    number;

v_ip_address   varchar2(30);

v_db_user_name   varchar2(20);

v_os_user      varchar2(20);

v_machine_name   varchar2(50);

v_terminal_name varchar2(50);

v_program_name   varchar2(50);

v_permit        varchar2(1);

v_rule_id       number;

v_check         varchar2(1) := 'Y';

begin

   begin

    select nvl(sid,0),nvl(sys_context('USERENV','IP_ADDRESS'),'NONE'),nvl(username,'NONE'),nvl(osuser,'NONE'),nvl(trim(machine),'NONE'),nvl(terminal,'NONE'),nvl(program,'NONE')

    into v_session_id,v_ip_address,v_db_user_name,v_os_user,v_machine_name,v_terminal_name,v_program_name

    from v$session

    where audsid=sys_context('USERENV','SESSIONID') and instr(nvl(program,'NONE'),'(J0')=0;

   exception when others then

     v_check := 'N';

   end;

   v_permit := 'D';

   if v_session_id>0 and v_check='Y'

   then

      begin

       --Permit Access

           select rule_id into v_rule_id from DBA_CONTROL_RULE where nvl(ip_address,v_ip_address)=v_ip_address and rownum=1;

           v_permit := 'P';

           exception when no_data_found then

           null;

       end;

      

       insert into dba_access_log(session_id,ip_address,db_user_name,os_user,machine_name,terminal_name,program_name,action_date)

                     values(v_session_id,v_ip_address,v_db_user_name,v_os_user,v_machine_name,v_terminal_name,v_program_name,sysdate);

       if v_permit = 'D'   then

          raise_application_error(-20001,'You(IP:'||v_ip_address||') have no priviledge to access the DB!please contact with DBA' );

       end if;

   end if;

end;

 

请使用浏览器的分享功能分享到微信等