oracle--08存储过程2

实际上是利用REF CURSOR

--procedure返回记录集:
----------------------声明一个Package--------------
CREATE OR REPLACE PACKAGE pkg_test
AS
TYPEmyrctypeIS REF CURSOR;

PROCEDURE get_r(p_id NUMBER,p_rc OUT myrctype); --Package中声明名为get 的Procedure(只有接口没内容)
END pkg_test;

-----------------声明Package Body,即上面Package中的内容,包括Procedure get---------------------
CREATE OR REPLACE PACKAGE BODY pkg_test
AS
PROCEDURE get_r(p_id NUMBER,p_rc OUT myrctype)
IS
sqlstr VARCHAR2 (500);
BEGIN
IF p_id = 0 THEN
OPEN p_rc FOR
SELECT ID, NAME, sex, address, postcode, birthday
FROM student;
ELSE
sqlstr :=
'select id,name,sex,address,postcode,birthday
from student where id=:w_id'; --w_id是个参数,
--以下 p_rc是个REF CURSOR游标类型,而且是OUT型参数,即可返回一个记录集了。USING p_id就是替换上面SQL中:w_id值拉:)
OPEN p_rc FOR sqlstr USING p_id; 
END IF;
END get;
END pkg_test;

--function返回记录集的例子,原理和上面相同,而是用function的return值来返回记录集。

函数返回记录集:
建立带ref cursor定义的包和包体及函数:

CREATE OR REPLACE
package pkg_test as

type myrctype is ref cursor;
function get_r(intID number) return myrctype;
end pkg_test;
/
CREATE OR REPLACE
package body pkg_test as
--函数体
function get_r(intID number) return myrctype is
rc myrctype; --定义ref cursor变量
sqlstr varchar2(500);
begin
if intID=0 then

--静态测试,直接用select语句直接返回结果
open rc for select id,name,sex,address,postcode,birthday from student;
else
--动态sql赋值,用:w_id来申明该变量从外部获得
sqlstr := 'select id,name,sex,address,postcode,birthday from student where id=:w_id';
--动态测试,用sqlstr字符串返回结果,用using关键词传递参数
open rc for sqlstr using intid;
end if;
return rc;
end get;
end pkg_test;

3、高程序运行效率,优化应用程序,在SP编写过程中应该注意以下几点:
a) SQL的使用规范:
i. 尽量避免大事务操作,慎用holdlock子句,提高系统并发能力。
ii. 尽量避免反复访问同一张或几张表,尤其是数据量较大的表,可以考虑先根据条件提取数据到临时表中,然后再做连接。
iii. 尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该改写;如果使用了游标,就要尽量避免在游标循环中再进行表连接的操作。
iv. 注意where字句写法,必须考虑语句顺序,应该根据索引顺序、范围大小来确定条件子句的前后顺序,尽可能的让字段顺序与索引顺序相一致,范围从大到小。
v. 不要在where子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
vi. 尽量使用exists代替select count(1)来判断是否存在记录,count函数只有在统计表中所有行数时使用,而且count(1)比count(*)更有效率。
vii. 尽量使用“>=”,不要使用“>”。 viii. 注意一些or子句和union子句之间的替换
ix. 注意表之间连接的数据类型,避免不同类型数据之间的连接。
x. 注意存储过程中参数和数据类型的关系。
xi. 注意insert、update操作的数据量,防止与其他应用冲突。如果数据量超过200个数据页面(400k),那么系统将会进行锁升级,页级锁会升级成表级锁。
b) 索引的使用规范:
i. 索引的创建要与应用结合考虑,建议大的OLTP表不要超过6个索引。
ii. 尽可能的使用索引字段作为查询条件,尤其是聚簇索引,必要时可以通过index index_name来强制指定索引
iii. 避免对大表查询时进行table scan,必要时考虑新建索引。
iv. 在使用索引字段作为条件时,如果该索引是联合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用。
v. 要注意索引的维护,周期性重建索引,重新编译存储过程。
c) tempdb的使用规范:
i. 尽量避免使用distinct、order by、group by、having、join、***pute,因为这些语句会加重tempdb的负担。
ii. 避免频繁创建和删除临时表,减少系统表资源的消耗。
iii. 在新建临时表时,如果一次性插入数据量很大,那么可以使用select into代替create table,避免log,提高速度;如果数据量不大,为了缓和系统表的资源,建议先create table,然后insert。
iv. 如果临时表的数据量较大,需要建立索引,那么应该将创建临时表和建立索引的过程放在单独一个子存储过程中,这样才能保证系统能够很好的使用到该临时表的索引。
v. 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先truncate table,然后drop table,这样可以避免系统表的较长时间锁定。
vi. 慎用大的临时表与其他大表的连接查询和修改,减低系统表负担,因为这种操作会在一条语句中多次使用tempdb的系统表。
d) 合理的算法使用:
根据上面已提到的SQL优化技术和ASE Tuning手册中的SQL优化内容,结合实际应用,采用多种算法进行比较,以获得消耗资源最少、效率最高的方法。具体可用ASE调优命令:set statistics io on, set statistics time on , set showplan on 等。


Oracle存储过程中执行DDL语法

--C1:删除目标表所有数据

EXECUTE IMMEDIATE 'TRUNCATE  TABLE  LDCODE';

--C2:创建索引

EXECUTE IMMEDIATE 'CREATE  INDEX  I_JHLCPOL_CONTNO ON JH_LCPOL(CONTNO)';

--C3:删除索引

EXECUTE IMMEDIATE 'DROP INDEX I_JHLCPOL_CONTNO';

    默认情况下,oracle对存储过程是使用所有者权限,也就是说:如果用户B条用A 用户下的存储过程,使用的是A用户的对象权限和系统权限。如果A用户没有权限的话,用户B执 行就会报错。
  所以第一种办法就是授予用户执行存储过程中DDL的权限。
  另一种办法是通过在存储过程中使用authid Current_user来讲存储过程转化为调用者权限。这样每次调用存储过程,都是动态根据调用者的权限构成去判定是否有权限。
eg:
create or replace procedure P_TEST
Authid Current_User
is
begin
execute immediate 'create table t (id number)';
end P_TEST;
/

create or replace procedure pr_zhaozhenlong_strsql
/*
名称:在存储过程中执行动态sql
功能:动态创建、删除表;
      动态修改表结构;
      动态修改表主键;
      判断某表、某列是否存在
调用:
      begin
        -- Call the procedure
        pr_zhaozhenlong_strsql;
      end;
创建人:赵振龙
创建时间:2007-01-03
*/
is
     v_rows      integer;
     v_sqlstr    varchar2(1000);
     v_tablename varchar2(50);
     v_pkname    varchar2(50);
begin
     --1、判断是否存在某表,删除、创建表
     --select * from tabs where table_name = 'TB_ZHAOZHENLONG'
     select count(1) into v_rows from tabs where table_name = 'TB_ZHAOZHENLONG';
     if v_rows >0 then
        execute immediate 'drop table tb_zhaozhenlong';
     end if;
     v_sqlstr := 'create table tb_zhaozhenlong("id" integer,rpt_date date,dept_id varchar2
(20),item varchar2(20), qty float)';
     execute immediate(v_sqlstr);
     select count(1) into v_rows from tabs where table_name = 'TB_TEMP_ZHAOZHENLONG';
     if v_rows >0 then
        execute immediate 'drop table tb_temp_zhaozhenlong';
     end if;
     
     --创建,基于会话的临时表
     v_sqlstr := 'create global temporary table tb_temp_zhaozhenlong(rpt_date date,dept_id 
varchar2(20),item varchar2(20), qty float, memo varchar(200))'
                 ||' on commit preserve rows';
     execute immediate(v_sqlstr);

     --2、判断是否存在某表,删除、创建表
     --select * from cols where table_name  = 'TB_ZHAOZHENLONG' and column_name = 'MEMO';
     select count(1) into v_rows from cols where table_name = 'TB_ZHAOZHENLONG' and 
column_name = 'MEMO';
     if v_rows <=0 then
        v_tablename :='tb_zhaozhenlong';
        v_sqlstr    := 'alter table ' ||v_tablename ||' add "demo" varchar2(100)';
        execute immediate(v_sqlstr);
     else
        execute immediate 'alter table tb_zhaozhenlong modify demo varchar2(200)';
     end if;
     --3、修改主键
     v_tablename :='TB_ZHAOZHENLONG';
     --第一步:增加列key_no
     v_sqlstr :='alter table '||v_tablename||' add key_no int';
     execute immediate(v_sqlstr);       
     --第二部:给key_no更新值
     v_sqlstr :='update '||v_tablename||' set key_no =rownum';
     execute immediate(v_sqlstr); 
     commit;       
     --第三步:将key_no置为非空
     v_sqlstr :='alter table '||v_tablename||'  modify key_no  int  not null';
     execute immediate(v_sqlstr);           
     --第四步:查找主键
     v_sqlstr :='select   count(1)'
                ||' from   user_constraints'   
                ||' where constraint_type=''P'' and  owner=user   and   
table_name='''||v_tablename ||'''' ;
     execute immediate(v_sqlstr) into v_rows;
     if v_rows >=1  then 
           v_sqlstr :='select   constraint_name'
                      ||' from   user_constraints'   
                      ||' where constraint_type=''P'' and  owner=user   and   
table_name='''||v_tablename ||'''' ;
           execute immediate(v_sqlstr) into v_pkname;
     end if;
     --第五步:删除主键
     if v_pkname  is  not null then 
         v_sqlstr := 'ALTER TABLE ' ||v_tablename ||' DROP CONSTRAINT '|| v_pkname ||' 
CASCADE';
         execute immediate(v_sqlstr);
     else
         v_pkname := 'pk_' ||v_tablename;
     end if;
     --第六步:增加主键
     v_sqlstr := 'ALTER TABLE ' ||v_tablename ||' ADD (CONSTRAINT '|| v_pkname ||' PRIMARY 
KEY(rpt_date,dept_id,item,key_no))';
     execute immediate(v_sqlstr);
end pr_zhaozhenlong_strsql;
/

dbms_metadata.get_ddl()用于获取对象的DDL,其具体用法如下。
注意:在sqlplus里,为了更好的展示DDL,需要设置如下参数:

set line 200
set pagesize 0
set long 99999
set feedback off
set echo off

1)获得表、索引、视图、存储过程、函数的DDL

select dbms_metadata.get_ddl('TABLE','TABLE_NAME','TABLE_OWNER') from dual;
select dbms_metadata.get_ddl('INDEX','INDEX_NAME','INDEX_OWNER') from dual;
select dbms_metadata.get_ddl('VIEW','VIEW_NAME','VIEW_OWNER') from dual;
select dbms_metadata.get_ddl('PROCEDURE','PROCEDURE_NAME','PROCEDURE_OWNER') from dual;
select dbms_metadata.get_ddl('FUNCTION','FUNCTION_NAME','FUNCTION_OWNER') from dual;

下面这个脚本用于获得某个schema下所有的表、索引、视图、存储过程、函数的DDL

set pagesize 0
set long 90000
set feedback off
set echo off
spool schema_ddl.sql
SELECT DBMS_METADATA.GET_DDL('TABLE',u.table_name,u.owner) FROM DBA_TABLES u;
SELECT DBMS_METADATA.GET_DDL('VIEW',u.view_name,u.owner) FROM DBA_VIEWS u;
SELECT DBMS_METADATA.GET_DDL('INDEX',u.index_name,u.owner) FROM DBA_INDEXES u;
select dbms_metadata.get_ddl('PROCEDURE',u.object_name, u.owner,) from dba_objects u where u.object_type = 'PROCEDURE';
select dbms_metadata.get_ddl('FUNCTION',u.object_name, u.owner,) from dba_objects u where u.object_type = 'FUNCTION';
spool off;

2)获得表空间的DDL
获得单个表空间的DDL:

select dbms_metadata.get_ddl('TABLESPACE','TBS_NAME') from dual;

获得所有表空间的DDL:

SELECT DBMS_METADATA.GET_DDL('TABLESPACE', TS.tablespace_name)
FROM DBA_TABLESPACES TS;

3)获得用户的DDL
获得单个用户的DDL:

select dbms_metadata.get_ddl('USER','EPAY_USER') from dual;

获得所有用户的DDL:

SELECT DBMS_METADATA.GET_DDL('USER',U.username)
FROM DBA_USERS U;


create or replace procedure spCreateTestTable
is
    v_CreateString varchar2(1000);
begin
    declare
        v_count number;
    begin
        v_count := 0;
        
        select count(*)
        into v_count
        from tab
        where tname='TEST_TABLE';
        
        if v_count=1 then
            dbms_output.put_line('test table already exists');
            v_CreateString := 'drop table test_table';
            execute immediate v_CreateString;
            commit;
        else
            dbms_output.put_line('test table created');
        end if;
        
        v_CreateString := 'create table test_table(' ||
                                                'aa varchar2(5), ' ||
                                                'bb varchar2(5))';
        execute immediate v_CreateString;
        commit;
    exception
   when others
   then
        rollback;
   end;
end;


存储过程是存放在数据字典中的程序块,它可以在不同用户和应用程序间共享,并可实现程序的优化和重用。
 
一、存储过程的创建和执行
1、利用SQL命令创建存储过程
语法格式如下:
 
Sql代码  
create [or replace] procedure [schema.]procedureName[(param1 mode1 dataType1,...n)]  
    is | as  
      var1 type1;  
      var2 type2;  
      ...  
    begin  
        statements; /*过程体,要执行的操作*/  
    end;  
 
其中,mode1表示参数的类型,跟方法的参数一样,有in、out和in out三种类型;dataType1表示参数的数据类型。
示例代码:
 
Sql代码  
create or replace procedure getModuleName(mid in number,mname out varchar)  
  as  
  begin  
    select name into mname from t_module where id=mid;  
  end;  
 
 
2、调用存储过程
语法格式如下:
 
Sql代码  
exec[ute] procedureName[(param1,...n)] /*这是在命令窗口可以这样执行*/  
 
注:
直接输入一个存储过程的名字也可以执行一个已定义的存储过程
示例代码:
 
Sql代码  
declare  
  mid number := 15;  
  mname varchar(20);  
begin  
  GETMODULENAME(mid,mname);  
  dbms_output.put_line(mname || ' ********ModuleName');  
end;  
 
 
 
二、存储过程的编辑修改
修改存储过程和修改视图一样,虽然也有alter procedure 语句,但它是用来重新编译存储过程的。如果要修改已经定义的存储过程,仍然使用create or replace procedure语句。
例如,修改上面的getmodulename存储过程如下:
 
Sql代码  
create or replace procedure getModuleName(mid in number,mname out varchar)  
  as  
  begin  
    if mid > 0 then  
        select name into mname from t_module where id=mid;  
    else  
        mname := null;  
    end if;  
  end;  
 
 
 
三、删除存储过程
当某个存储过程不再需要时,可以将它删除,以释放其占用的内存资源。
语法格式如下:
 
Sql代码  
drop procedure [schema.]procedureName;  
 
例如删除getmodulename存储过程如下:
 
Sql代码  
drop procedure getmodulename;  

猜你喜欢

转载自zhyp29.iteye.com/blog/2302507