Oracle学习笔记 5 游标和触发器


游标:
隐式游标:%FOUND, %NOTFOUND ,%ROWCOUNT
1.%FOUND 用法,只有在DML 语句影响一行或者多行时,%FOUND 属性才返回 TRUE。下列示例演示了 %FOUND 的用法:
begin
  update employees2 set first_name = first_name ||' t' where employee_id = 2;
if SQL%found then
  dbms_output.put_line('数据已经更新');
       --  dbms_output.put_line('rowCount = '||mrowcount);
else
  dbms_output.put_line('数据没有找到');
end if;
end;
/
以下代码演示了创建了一个游标,返回employees2 表中 salary 大于300000 的记录,注意type 的使用:
    declare
   csalary employees2.salary%type;
   cursor emp2_cursor is select salary from employees2 where salary >300000;
begin
  open emp2_cursor ;
  loop
    fetch emp2_cursor into csalary;
    exit when emp2_cursor%notfound;
    dbms_output.put_line('csalary = '||csalary);
  end loop;
end;
/


以下代码演示了创建了一个游标,返回employees2 表中 division_id=’SAL’ 的记录。
注意rowtype 的使用:
declare
cursor employee2_cursor is select * from employees2 where division_id='SAL';
  myrecord employees2%rowtype;
begin
  open employee2_cursor;
  fetch employee2_cursor into myrecord;
  while employee2_cursor%found loop
    dbms_output.put_line('employee id ='||myrecord.employee_id);
    dbms_output.put_line('first Name  ='||myrecord.first_name);
    dbms_output.put_line('last name ='||myrecord.last_name);
    fetch employee2_cursor into myrecord;
end loop;
end;
/
以下代码演示了带参数的游标,根据division id 查询指定的记录:
declare
  myrecord employees2%rowtype;
  cursor emp_cursor(divisionid varchar2) is select * from employees2 where division_id =divisionid;
begin
  open emp_cursor('&divisionid');
--loop
  fetch emp_cursor into myrecord;
  while emp_cursor%found loop
-- exit when emp_cursor%notfound;
  dbms_output.put_line('employee id = '||myrecord.employee_id);
  dbms_output.put_line('division id = ' ||myrecord.division_id);
  dbms_output.put_line('first name = ' ||myrecord.first_name);
  fetch emp_cursor into myrecord;
end loop;
close emp_cursor;
end;
/
以下代码演示了如何更新 employees2 表中的 first_name 字段:
set serveroutput on
declare
firstName varchar2(20);
cursor employees2_cursor is select first_name from employees2 where employee_id=1 for update of
first_name;
begin
   open employees2_cursor;
   loop
    fetch employees2_cursor into firstName;
    exit when employees2_cursor%notfound;
   update employees2
   set first_Name='jeff ' where current of employees2_cursor;
  end loop;
close employees2_cursor;
commit;
end; /
触发器:
触发器是当特定事件出现时自动执行的存储过程
特定事件可以是执行更新的DML语句和DDL语句
触发器不能被显式调用
触发器的功能:
自动生成数据
自定义复杂的安全权限
提供审计和日志记录
启用复杂的业务逻辑
创建触发器语法:
CREATE [OR REPLACE] TRIGGER trigger_name
AFTER | BEFORE | INSTEAD OF
[INSERT] [[OR] UPDATE [OF column_list]]
[[OR] DELETE]
ON table_or_view_name
[REFERENCING {OLD [AS] old / NEW [AS] new}]
[FOR EACH ROW]
[WHEN (condition)]
pl/sql_block;

创建触发器,以下代码演示了插入或者修改 employees2 表中的first_name 如果等于 ‘scott’时触发器就会执行:

create or replace trigger tri_employees2
before insert or update of first_name
   on employees2
  referencing NEW as newdata OLD as olddata
for each row
when (newdata.first_name='scott')
  begin
        :newdata.salary :=20000;
     dbms_output. _line('new.salary:' || :newdata.salary);
     dbms_output.put_line('old.salary:' || lddata.salary);
  end;
执行以上触发器:
insert into employees2 values(38,'SUP','WOR','scott','mp',50000);
或者:
update employees2 set salary=90000,first_name='scott' where employee_id=38;





以下代码针对数据完整性进行操作:
  删除操作:
         create or replace trigger del_deptid
after delete on dept
for each row
begin
   delete from employee where deptid = ld.id;//old.id表示主键表中的ID
end del_deptid;
/
执行以上触发器:
   delete from dept where id=1;  查看employee 表中的 deptid 记录;
添加操作:
    create or replace trigger insert_dept
after insert on dept
for each row
begin
insert into employee(id,name,deptid) values('6','chenmp',:new.id);
end;
/
   执行以上触发器:
insert into dept values(6,'销售部门');
  查看employee 表中的 deptid 记录


修改操作:
  create or replace trigger update_dept
after update on dept
for each row
  begin
   update employee set deptid = :new.id where deptid = ld.id;
end;
/
执行以上触发器:
update dept set id=8 where id=1;
查看employee 表中的 deptid 记录


以下代码演示了行级触发器:
创建表:
drop table rowtable;
      create  table rowtable (id number(8) , name varchar2(100));
创建序列
        create sequence rowtablesequence;
创建触发器:
create or replace trigger set_sequence
before insert on rowtable
for each row
declare
    rsequence number(8);
begin
select rowtablesequence.nextval into rsequence from dual;
   :NEW.id :=rsequence;
end;
/
执行SQL语句:
     insert into rowtable values(232,'scott');
以下代码演示了语句级触发器:
创建表:
create table mylog(curr_user varchar2(100),curr_date date,opera varchar2(10));
创建触发
create or replace trigger tri_mylog
after insert or delete or update on employees2
begin
if inserting then
insert into mylog values(user,sysdate,'insert');
elsif deleting then
insert into mylog values(user,sysdate,'delete');
else
insert into mylog values(user,sysdate,'update');
end if;
end;
/
INSTEAD OF 触发器
   INSTEAD OF 触发器是在视图上而不是在表上定义的触发器,它是用来替换所使用实际语句的触发器。
   以下代码创建了视图:
create view employee_job as
select e.job_id,e.employee_id,e.first_name,e.last_name,j.name
from employees2 e,jobs j where e.job_id = j.job_id;
以下代码创建 INSTEAD OF 触发器。
create or replace trigger tri_view
instead of insert on employee_job
for each row
begin
  insert into jobs values(:new.job_id,:new.name);
  insert into employees2(employee_id,first_name,last_name,job_id) values(:new.employee_id,:new.first_name,:new.last_name,:new.job_id);
end;
/
执行以下语句查看操作:
    insert into employee_job values('OTH',43,'abc','dd','OTHER');


模式触发器:可以在模式级的操作上建立触发器,如:create ,alter,drop,grant,revoke 和truncate 等 DDL语句:
以下示例对用户所删除的所有对象进行日志记录。
1. 创建数据库表:
  drop table dropped_obj;
     CREATE TABLE dropped_obj
(
  obj_name VARCHAR2(30),
  obj_type VARCHAR2(20),
  drop_date DATE
);
    2.创建触发器:
CREATE OR REPLACE TRIGGER log_drop_obj
AFTER DROP ON SCHEMA
BEGIN
  INSERT INTO dropped_obj
  VALUES (ORA_DICT_OBJ_NAME, ORA_DICT_OBJ_TYPE, SYSDATE);
END;
/
   3.创建和删除对象:
          创建对象:CREATE  TABLE  for_drop ( x CHAR );
          删除对象:DROP  TABLE  for_drop;
4.查看日志表中的信息:
         SELECT  *  FROM  dropped_obj;



起用和禁用触发器:
   以下代码演示了禁用biu_emp_deptno 触发器:
         ALTER TRIGGER biu_emp_deptno DISABLE;
   以下代码演示了启用biu_emp_deptno 触发器:  
         ALTER TRIGGER biu_emp_deptno enable;

可以使用:
        Alter table table_name{enable | disable} all triggers;
禁用或者起用在特定表上建立的所有触发器。

删除触发器:
        Drop trigger trigger_name;

查看触发器信息,可以使用user_trigers 数据字典视图。
Desc user_triggers



在使用delete语句删除数据时,数据库是要做日志记录的,以便将来可以恢复数据,可是我在删除上百万条数据时,十分缓慢甚至死机,请问有没有什么好方法?
  网友观点一:
create or replace procedure delete_table
is
i number(10);
begin
  for x in (select * from emp where DEPTNO like 'a%')
  loop
      delete emp where emp.id = x.id
      i:=i+1;
      if i>1000 then
         commit;
         i:=0;
      end if;
  end loop;
exception
    when others then
         dbms_out.put_line(SQLcode);
         rollback;
end delete_table;
  网友观点二:
这个是我平常用来批量删除数据,每500条数据提交一次。
DECLARE
CNT NUMBER(10):=0;
I NUMBER(10);
BEGIN
SELECT COUNT(*) INTO CNT FROM ep_arrearage_bak WHERE TO_CHAR(DF_DATE,'MM')='01';
FOR I IN 1..TRUNC(CNT/500)+1 LOOP
DELETE FROM ep_arrearage_bak WHERE TO_CHAR(DF_DATE,'MM')='01' AND ROWNUM<=500;
COMMIT;
END LOOP;
END;
专家意见:几个办法:
  1. 如果删除的数据是大部分,建议使用楼上的方法把要保留的数据放在一个临时表里,truncate table后再放回来
  2. 也可以分段提交,楼上也提到了
  3. 专门使用一个大回滚段
  4. 如果确认将来不需要做恢复,改为非归档模式,删除完改回来再做个备份.
  专家给出的解决方案:
有条件的分步删除数据表中的记录
--创建测试表
create table test as select * from dba_objects;
Table created.
--创建删除表的存储过程
create or replace procedure deleteTab
--插入语句
   SQL> insert into test select * from dba_objects;
6374 rows created.
SQL> /
6374 rows created.
SQL> /
6374 rows created.
SQL> commit;
--创建删除的存储过程
create or replace procedure deleteTab
  /**
   ** Usage: run the script to create the proc deleteTab
   **        in SQL*PLUS, type "exec deleteTab('Foo','ID>=1000000','3000');"
   **        to delete the records in the table "Foo", commit per 3000 records.
   **       Condition with default value '1=1' and default Commit batch is 10000.
   **/
  (
    p_TableName    in    varchar2,    -- The TableName which you want to delete from
    p_Condition    in    varchar2 default '1=1',    -- Delete condition, such as "id>=100000"
    p_Count        in    varchar2 default '10000'    -- Commit after delete How many records
  )
  as
   pragma autonomous_transaction;
   n_delete number:=0;
  begin
   while 1=1 loop
     EXECUTE IMMEDIATE
       'delete from '||p_TableName||' where '||p_Condition||' and rownum <= :rn'
     USING p_Count;
     if SQL%NOTFOUND then
     exit;
     else
          n_delete:=n_delete + SQL%ROWCOUNT;
     end if;
     commit;
   end loop;
   commit;
   DBMS_OUTPUT.PUT_LINE('Finished!');
   DBMS_OUTPUT.PUT_LINE('Totally '||to_char(n_delete)||' records deleted!');
  end;
  /
--执行语句
SQL> exec deleteTab('TEST','object_id >0','10000')



oracle上有条件的百万条数据删除方法
myrtle 发表于 2006-5-12 1:42:00

1、建一个临时表,把要保存的纪录插入到临时表,TRUNCATE原表,然后把临时表中的数据倒回。   (在一个不带索引的表中插入几百万纪录其实是很快的,费时以分钟计。如果有索引,先禁用索引或者删除索引,装载完后重建)
2、用exp/imp,先用exp     query='满足保存条件"备份。然后truncate表,然后将备份文件imp回去
3、对于分区表可以直接truncate partition 或者drop partition
alter table tablename truncate partition partitionname;
===========================
土方法:(影响删除的要素是:rollback segment size, log, index for where statments)
  在其它机器上建一个临时用的数据库;  
  把要删除的表导出来,再导入临时用的数据库,在临时数据库中作以下操作:  
  0.   根据delete   的条件建立合适的索引,删除其它没有用的索引和约束;  
  1.   写一个过程删除若干条数据后提交;(   要点在于批量提交可减少rooloback segment的需求以及加快速度)
  2.   删除完成后,删掉表的索引和约束关系;  
  3.   导入原数据库;  
  4.   重建索引和约束。

实例:
SQL> select count(1) from t_customer_log;
  COUNT(1)
----------
  25076317
有2500多万条记录
SQL> select count(1) from t_customer_Log where start_time>=20060312000000;
  COUNT(1)
----------
   9775788
有近1000
SQL> create table t_customer_log_t nologging  as select * from t_customer_Log where start_time>=2006031200000;
(要注意nologging选项,可加 on tablespace XXX nologging指定表空间。) 
9:54--10:06用了12分钟完成了1000万条数据插入!
drop table t_customer_log
alter table t_customer_log t rename to t_customer_log
注意:alter table t_customer_log_t rename t_customer_log可能需要重建索引,存储过程、触发器等
(查询表相关索引:
select index_name,index_type,table_name,table_type, UNIQUENESS from dba_indexes where table_name='T_CUSTOMER_LOG';
查询表相关的触发器:
查询表相关的约束(因此,比较好用truncate,不要用drop,引起太多相关处理的麻烦。但如果倒入数据量很大时,是可以考虑这种操作的。)
注意使能触发器!
*将目标表设成nologging, 将目标表上的索引,约束,触发器
先禁用, 然后使用直接路径方式插入数据, 可使用
set timing on来测试时间
注意: 这种方式操作过程中如果发生错误,将不能恢复.
如果数据库已经是archive log 模式,设置nologging 不起
作用.
)
也可用:
SQL> truncate table t_customer_Log
不到一分钟truncate!
SQL> insert into t_customer_Log select * from t_customer_Log_t;
SQL>drop table t_customer_Log_t;
清除执行delete后的空间
delete与truncate都是把表的的数据清空.但它们是有区别的.
DELETE 把数据清除后可以rollback,但TRUNCATE不可ROLLBACK.
DELETE 是属DML ,TRUNCATE是DDL.
DELETE 删除数据后不会回收空间,即如果原来的table已占了10M,你删除了2M的记录,这个表公然还是占10M. TRUNCATE在清空数据后可以回收空间,即 high water mark会降下来.
TRUNCATE不激活任何DELETE TRIGGER.
PS:
在你用delete清除记录后,可以用
alter table table_name deallocate unused;来回收没用的空间.



你可能感兴趣的:(oracle,sql,Opera,J#)