【IT168 技术文档】
ORACLEERP开发基础之前言
http://tech.it168.com/a2009/0427/274/000000274048.shtml
ORACLE ERP开发之OracleForms基础(一)Forms设置部分
http://tech.it168.com/a2009/0428/274/000000274178.shtml
ORACLEERP开发基础之OracleForms基础(二)FORMS代码部份
http://tech.it168.com/a2009/0428/274/000000274236.shtml
ORACLE ERP开发基础之Oracle Report基础
http://tech.it168.com/a2009/0429/274/000000274343.shtml
常用函数
substr函数
Example:SELECT substr('teach',0,3) FROM DUAL
Effect: tea
Example: SELECT substr('teach',3,3) FROM DUAL
Effect: ach
Lpad/Rpad函数
Example: SELECT LPAD('TEACH',4) FROM DUAL
Effect: teac
Example: SELECT LPAD('TEACH',10,'5') FROM DUAL
Effect: 55555TEACH
日期函数
Oracle的日期函数比较丰富,不象SQL SERVER只有dateadd、datediff之类。
1.系统时间
SQL:select getdate() value
Oracle:select sysdate value from dual
2.前后几日
都支持直接与整数相加减
3.求日期
SQL:select convert(char(10),getdate(),20) value
Oracle:select trunc(sysdate) value from dual
select to_char(sysdate,'yyyy-mm-dd') value from dual
4.求时间
SQL:select convert(char(8),getdate(),108) value
Oracle:select to_char(sysdate,'hh24:mm:ss') value from dual
5.取日期时间的其他部分
SQL:DATEPART 和 DATENAME 函数 (第一个参数决定)
Oracle:to_char函数 第二个参数决定
下表补充说明SQL与ORACLE在取参数时的区别
6.当月最后一天
SQL:比较烦琐,先求当月的第一天,然后求得下月的第一天,最后减一得到当月最后一天。
Oracle:select LAST_DAY(sysdate) value from dual
7.本星期的某一天(比如星期日)
SQL:week函数
Oracle:SELECT Next_day(sysdate,7) vaule FROM DUAL;
8.字符串转时间
SQL:可以直接转或者select cast('2004-09-08'as datetime) value
O:SELECT To_date('2008-01-05 22:09:38','yyyy-mm-dd hh24-mi-ss') vaule FROM DUAL;
9.求两日期某一部分的差(比如秒)
SQL:select datediff(ss,getdate(),getdate()+12.3) value
Oracle:直接用两个日期相减(比如d1-d2=12.3)
SELECT (d1-d2)*24*60*60 vaule FROM DUAL;
10.根据差值求新的日期(比如分钟)
SQL:select dateadd(mi,8,getdate()) value
Oracle:SELECT sysdate+8/60/24 vaule FROM DUAL;
11.当月第一天
SQL:select dateadd(getdate,-day)
O: select trunc(sysdate,'mm') from dual;
随机取前10条不同的记录
Oracle有提供一个函数来实现取随机数:DBMS_RANDOM
SELECT DBMS_RANDOM.VALUE FROM DUAL;
返回0--1之间的随机数,因为DBMS_RANDOM是默认使用时钟作为种子,来实现取随机数的。
select * from(select * from hek_test_tb order by dbms_random.value(1,10)) where rownum<10
TRUNC函数
Oracle与SQL SERVER在日期比较方面有重大区别。
例:含有日期+时间的字段BEGINDATE与仅含有日期的字段在比较时ENDDATE。
SQL SERVER:BEGINDATE<=ENDDATE
ORACLE:TRUNC(BEGINDATE,‘DD‘)<=ENDDATE ORACLE必须先截断时间,然后再进行比较。如果没有这样做,这将会是一个巨大的BUG。
修改表的一些常用语法
添加列:alter table hek_test_headers add col_test number;
修改列:alter table hek_test_lines modify litem varchar(40) not null;
删除列:alter table hek_test_lines drop columns col_test;
重命名列:alter table hek_test_lines rename column col_test to col_test2;
添加主键:alter table HEK_TEST_LINES add constraint pk_test primary key (LINEID);
添加外键:alter table hek_test_lines add constraint fk_test foreign key(hid) references hek_test_headers(hid)
失效主键:alter table hek_test_lines disable constraint pk_test;
失效外键:alter table hek_test_lines disable constraint fk_test;
删除主键:alter table hek_test_lines drop constraint pk_test cascade;
删除外键:alter table HEK_TEST_LINES drop constraint fk_test;
舍入函数
三个舍入函数:round()、floor()、ceil()
Round():实现四舍五入,允许设置保留的位数,这个也最常用的四舍五入函数。
Floor():实现取整。一般的程序语言是整数除以整数,返回的仍是整数。PL/SQL想得比较多。
Ceil():实现近似值。Ceil会直接近似取整,如果想保留小数,就要自己动手写个函数了。
select 9/4 from dual;
select CEIL(9/4) from dual;
select round(9/4,0) from dual;
实现类似BREAK语句
在没有LOOP…END LOOP时,是不能使用EXIT的。但可以通过GOTO语句实现。
declare
t integer;
begin
t:=&t;
DBMS_OUTPUT.PUT_LINE('T='||t);
if t=1
then DBMS_OUTPUT.PUT_LINE('Goto!');
goto GOTOS;
else
DBMS_OUTPUT.PUT_LINE('NO Goto!');
goto NoGoto;
end if;
<> for i in 1..10 loop
DBMS_OUTPUT.put_line('i='||i);
end loop test;
<> NULL;
end;
Oracle定时器
ORACLE 9i及其以前版本,都是使用DBMS_JOB来实现任务调度。10g官方推荐使用DBMS_SCHEDULER。
1.1 JOB创建
1.1.1 先创建一个存储过程
create or replace procedure usp_test_pr
is
BEGIN update t_test set tname='test' where tid =110;
END;
1.1.2 在pl/sql developer中创建job
declare
v_job number;
begin
sys.dbms_job.submit(job=>v_job,
what => 'usp_test_pr;',
next_date => to_date('22-12-2008', 'dd-mm-yyyy'),
interval => 'sysdate+1/1440'); --每隔一分钟执行一次
commit;
end;
1.2 删除JOB
exec DBMS_JOB.remove(JOB=>&job_number);--输入job_number
commit;
1.3 查询所有的JOB
select * from dba_jobs j order by j.JOB desc
Over分析查询
Over函数,其实也可以转换成嵌套查询来实现。
准备测试数据
create table t_test( tid int, tname varchar2(20), tsalary number(8,2), tdeptno int, primary key(tid) );
begin
insert into t_test values(1,'小王',4500.21,3);
insert into t_test values(2,'小张',4200,3);
insert into t_test values(3,'小K',3000,3);
insert into t_test values(4,'小Q',8500.5,4);
insert into t_test values(5,'小T',1520.5,4);
insert into t_test values(6,'小丁',3000,5);
insert into t_test values(7,'小李',3000,5);
insert into t_test values(8,'小KK',3000,5);
end;;
SELECT * FROM T_TEST;
-----------------------------------------------------------------------------------------------
1 1 小王 4500.21 3
2 2 小张 4200.00 3
3 3 小K 3000.00 3
4 4 小Q 8500.50 4
5 5 小T 1520.50 4
6 6 小丁 3000.00 5
7 7 小李 3000.00 5
8 8 小KK 3000.00 5
--求工资占部门总工资额的比率
select tname, tsalary,tsalary/sum(tsalary)over(partition by tdeptno) per from t_test
-------------------------------------------------------------------------------------------
1 小王 4500.21 0.38462642978203
2 小张 4200.00 0.358967915960483
3 小K 3000.00 0.256405654257488
4 小Q 8500.50 0.848268635864684
5 小T 1520.50 0.151731364135316
6 小丁 3000.00 0.333333333333333
7 小李 3000.00 0.333333333333333
8 小KK 3000.00 0.333333333333333
--当然也可以不使用over,实现同样的效果
select a.tname,a.tsalary,a.tsalary/b.ttl per,a.tdeptno
from t_test a,(select tdeptno,sum(tsalary) ttl from t_test group by tdeptno) b
where a.tdeptno=b.tdeptno
1 小王 4500.21 0.38462642978203 3
2 小张 4200.00 0.358967915960483 3
3 小K 3000.00 0.256405654257488 3
4 小Q 8500.50 0.848268635864684 4
5 小T 1520.50 0.151731364135316 4
6 小丁 3000.00 0.333333333333333 5
7 小李 3000.00 0.333333333333333 5
8 小KK 3000.00 0.333333333333333 5
--求工资排名
SELECT ROWNUM ser ,TNAME,TSALARY,TDEPTNO
FROM(SELECT * FROM t_test order by tsalary desc)
--注意这样求出的排名,有点问题。就是工资一样的没有处于第一排名,这是由rownum的性质决定。
----------------------------------------------------------------------------------------------------
1 小Q 8500.50 4
2 小王 4500.21 3
3 小张 4200.00 3
4 小K 3000.00 3
5 小KK 3000.00 5
6 小李 3000.00 5
7 小丁 3000.00 5
8 小T 1520.50 4
--要实现真正的排名,应该使用rank或者dense_rank
-- rank()和dense_rank()的区别是:
--rank()是跳跃排序,有两个第二名时接下来就是第四名
--dense_rank()l是连续排序,有两个第二名时仍然跟着第三名
select dense_rank()over(order by tsalary desc) ser,tname,tsalary,tdeptno from t_test
------------------------------------------------------------------------
1 小Q 8500.50 4
2 小王 4500.21 3
3 小张 4200.00 3 4 小K 3000.00 3
4 小KK 3000.00 5
4 小李 3000.00 5
4 小丁 3000.00 5
5 小T 1520.50 4
--上面是工资在全体部门的排名,如果要求部门排名的话。
select tname,tsalary,tdeptno,dense_rank()over(partition by tdeptno
order by tsalary desc ) ser
from t_test
------------------------------------------------------------------------
1 小王 4500.21 3 1
2 小张 4200.00 3 2
3 小K 3000.00 3 3
4 小Q 8500.50 4 1
5 小T 1520.50 4 2
6 小丁 3000.00 5 1
7 小李 3000.00 5 1
8 小KK 3000.00 5 1
--直接实现行汇总
select tname,tsalary,tdeptno,sum(tsalary)over(partition by null) ttl from t_test
如下:
1 小王 4500.21 3 30721.21
2 小张 4200.00 3 30721.21
3 小K 3000.00 3 30721.21
4 小Q 8500.50 4 30721.21
5 小T 1520.50 4 30721.21
6 小丁 3000.00 5 30721.21
7 小李 3000.00 5 30721.21
8 小KK 3000.00 5 30721.21
Oracle层次树查询
Oracle层次树是通过Connect by [条件] Start with [条件] 来实现。这一功能非常好用,比如ERP中的BOM、HR中的组织架构,就算是这类的典型应用了。不过,Oracle EBS11i中好象没实现,BOM也没有使用树这种组件。
下面就做一个简单的MRP试算过程,来说明层次树的应用。
1.1 建一个简单BOM表。
create table hek_bom( master_id varchar2(20), master_name varchar2(50), sub_id varchar(20), sub_name varchar(20) )
1.2 放入测试数据。
begin
insert into hek_bom values('0001','V1卡车','10001','V1发动机');
insert into hek_bom values('0001','V1卡车','10002','V1车架'); i
nsert into hek_bom values('0001','V1卡车','10003','V1车轮');
insert into hek_bom values('0002','V2卡车','10001','V1发动机');
insert into hek_bom values('0002','V2卡车','10002','V1车轮');
insert into hek_bom values('0002','V2卡车','10004','V2车架');
insert into hek_bom values('0003','V3卡车','10004','V1发动机');
insert into hek_bom values('0004','V4卡车','10005','V2发动机');
insert into hek_bom values('10001','V1发动机','10006','V1活塞');
insert into hek_bom values('10001','V1发动机','10007','V1火花器');
insert into hek_bom values('10007','V1活塞','10008','V1橡胶片');
insert into hek_bom values('10007','V1活塞','10009','V1螺丝');
end;
1.3 查询一下明细:
select t.* from hek_bom t for update
------------------------
1 0001 V1卡车 10001 V1发动机
2 0001 V1卡车 10002 V1车架
3 0001 V1卡车 10003 V1车轮
4 0002 V2卡车 10001 V1发动机
5 0002 V2卡车 10002 V1车轮
6 0002 V2卡车 10004 V2车架
7 0003 V3卡车 10004 V1发动机
8 0004 V4卡车 10005 V2发动机
9 10001 V1发动机 10006 V1活塞
10 10001 V1发动机 10007 V1火花器
11 10006 V1活塞 10008 V1橡胶片
12 10006 V1活塞 10009 V1螺丝
1.4 问题:求V1螺丝料品有哪几层产品用到。
select level,t.* from hek_bom t connect by prior t.master_id=t.sub_id start with t.sub_id='10009'
--注意这条SQL语名的语法,connect by prior t.master_id=t.sub_id表示优先从子节点到父节点。
--start with t.sub_id='10009'相当于where t.sub_id='10009'
---------------------------------------------
1 10006 V1活塞 10009 V1螺丝
2 10001 V1发动机 10006 V1活塞
3 0001 V1卡车 10001 V1发动机
4 0002 V2卡车 10001 V1发动机
1.5 问题:求:V1卡车的BOM结构:
select level,t.* from hek_bom t connect by prior t.sub_id=t.master_id start with t.master_name='V1卡车'
--connect by prior t.sub_id=t.master_id表示优先从父节点查询到子节点。
---------------------------------------------------------------------------------------------
1 0001 V1卡车 10001 V1发动机
2 10001 V1发动机 10006 V1活塞
3 10006 V1活塞 10008 V1橡胶片
4 10006 V1活塞 10009 V1螺丝
5 10001 V1发动机 10007 V1火花器
6 0001 V1卡车 10002 V1车架
7 0001 V1卡车 10003 V1车轮
―――――――――――――――――――――――――――――――――――――
通过这两个例子,已经可以很形象地说明connect by 的典型应用了。
Merge into应用
Merge into适用于数据量非常大的表,做insert\update动作。比起insert into select效率上要更高些。当然merge into也提供了when matched then的条件规范。
基本语法:
Truncate table
Truncate table与delete * from table作用是一样,都是删除表中全部数据。但Delete是与事务关联的,所以Truncate table会快很多。另外Oracle书上说,truncate 会把 highwatermark 回归至 0 ,当下一次再插入新资料时就会快一些。这个功能有时间可以测试一下。需要注意的是Truncate table不是PL/SQL直接使用,必须使用动态SQL来执行,并且truncate table是无须commit语句的。
begin
execute immediate 'truncate table hek_table';
end;
取不重复的记录行
表结构:table
ID NAME
110 AA
120 AA
125 BB
126 BB
129 CC
取得如下记录:
110 AA
125 BB
129 CC
如果是MySQL实现这个比较简单。因为MySQL Group By支持多字段。
Select ID,NAME from TABLE group by NAME 但Oracle不支持此用法。
可以做到的,目前只有Select NAME,MAX(ID) FROM TABLE GROUP BY NAME EBSOracle
统计EBS表数量及Oracle概念
转到Oracle ERP有段时间了,突然被一个问题问倒。Oracle ERP有多少张表?统计表的数量首先想到数据字典,可是select了半天,发现对一些概念有点乱。特总结如下:
首先,要明白几个Oracle核心概念。这些概念2年前在读《Oracle8i初学者指南》中明白了一些,无耐后来转做JAVA开发,这些东西又还给作者了^_^。
―解释数据库、表空间、数据文件、表、数据的最好办法是想象一个装满东西的柜子。数据库就是柜子,柜中的抽屉是表空间,抽屉中的文件夹是数据文件,文件夹中的纸是表,写在纸上的信息就是数据‖。 这是《Oracle8i初学者指南》关于这些概念的理解。这样理解并没有错,但事实远非如此简单。
1.核心概念:数据库(Scheme)、数据文件、表空间(Tablespace)、表(table)、用户(user)。
1.1数据库是数据文件、控制文件、日志文件等组成的。属于物理文件的范畴。一个Scheme可以多个Tablespace。对于不同的应用,Oracle也推荐创建不同的表空间,以提高性能。例:DBA备份时可以按表空间进行备份。
1.2表空间是逻辑文件的范畴,Scheme是通过Tablespace进行管理的。这也是Oracle与SQL SERVER在设计理念上的最大区别。一个表空间一般只对应一个数据文件,但也可以有多个数据文件。例:给表空间增容。
1.3表也是逻辑文件的范畴。一个表空间可以多张表,一张表一般只能属于一个表空间。当然Oracle也有提供分区表,来实现一张表存放在多个表空间中。
1.4一张表只能属于一个用户。即用户与表是1对多的关系。Oracle是通过synonym来实现不同用户创建的表进行互访的。
1.5一个用户通过配额(Quota)可以拥有多个表空间,但只能有一个默认的表空间。 引申一下数据字典的概念,数据字典就是存放Oracle数据对象的表。一般是通过数据字典视图来查询,有三种视图:user_、all_、dba_。
2.查看全部表的数量
select count(*) from dba_tables -–因为有做过大量的二次开发,所以直接这样得到的数量是不准确的。
3.查看当前用户所有表的数量
select count(*) from all_tables
4.查看当前用户创建表的数量
select count(*) from user_tables
5.ebs11i对应的表空间是APPS_TS_TX_DATA,通过对表空间进行表的统计,算是比较准确的。
select count(*) from dba_tables dt where dt.tablespace_name='APPS_TS_TX_DATA'
得到EBS表的数量为“15997”。
Oracle的基本概念
EBS死锁之解决办法
1.查看死锁的进程
SELECT c.owner
,c.object_name ,
c.object_type
,fu.user_name locking_fnd_user_name
,fl.start_time locking_fnd_user_login_time
,vs.module
,vs.machine
,vs.osuser
,vlocked.oracle_username
,vs.SID
,vp.pid
,vp.spid AS os_process
,vs.serial#
,vs.status
,vs.saddr
,vs.audsid
,vs.process
FROM fnd_logins fl
,fnd_user fu
,v$locked_object vlocked
,v$process vp
,v$session vs
,dba_objects c
WHERE vs.SID = vlocked.session_id AND vlocked.object_id = c.object_id
AND vs.paddr = vp.addr AND vp.spid = fl.process_spid(+) AND vp.pid = fl.pid(+) AND fl.user_id = fu.user_id(+)
--AND c.object_name LIKE '%' || UPPER('&tab_name_leaveblank4all') || '%' AND NVL(vs.status ,'XX') != 'KILLED';
2.Kill掉进行程
alter system kill session 'SID,serial#';
--SID、serial# 可以从上面的查询结果中找到。
PL/SQL过程调用表示
PL/SQL过程调用支持两种参数表示方式,一种是位置表示法,如C、JAVA都是使用这种方式。第二种是名称表示法,这个恐怕只有PL/SQL使用,当然PL/SQL也是从Ada那边学来的。 名称表示法示例(注:仅限于过程,名称表示法不适用于函数。切记!):
创建存储过程:
create or replace procedure test_proc(i_temp number,j_temp number,c_temp varchar)
is
t varchar(30);
begin t := i_temp || '' || j_temp || c_temp;
dbms_output.put_line(t);
end;
调用:
begin test_proc(j_temp=>10,i_temp => 60,c_temp => 'test');
end;
PL/SQL调用Java类
作为JAVA程序员,对ORACLE的javasource一直是想找个机会下手的。呵呵!风高月夜,此时正下手之机。Oracle有提供一个叫“外部例程”来实现与其他编程语言的访问,但ORACLE特别集成了JAVA。例如:可以将业务逻辑写在JAVA类中,通PL SQL进行调用,从而实现了业务逻辑层与数据库分离。
1.HelloWorld例子
1.0创建java source
create or replace and compile java source named JTest as
public class JTest
{
public static String entry(String s,int i)
{
return "First "+i+" hello"+s;
}
}
1.1创建调用function
create or replace function read_string (t in varchar2,i number)
return varchar2 is language java name ' JTest.entry(java.lang.String,int) return t';
--注:function的形参应与调用的JAVA类中方法的形参相同
1.2调用测试
SELECT read_string(' world',10) from dual
注意:在ORACLE中,JAVA SOURCE可以拥有非static方法,但PLSQ/包调用的方法必须是static的。
2.再来一个实用一点的例子(JDBC) 2.1创建JAVA类
create or replace and compile java source named testjdbc as
import java.io.IOException; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.PreparedStatement; i
mport java.sql.ResultSet; import java.sql.SQLException;
/**
* @author jarwang
* @since 2008-6-12
* @version xx1.0
*/
public class TestJDBC{
public static String queryTable(){ String temp="";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null; try{ conn = getConn();
pstmt = conn.prepareStatement("select hname from test ");
rs = pstmt.executeQuery();
while(rs.next()){ temp += rs.getString("hname");
} }catch(Exception ioex){ System.out.println("failure: "+ioex);
}finally{ closeConn(conn,pstmt,rs);
} return temp;
} public static void insertTable(String s){ Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = "insert into test (hname)values('"+s+"')";
try{ conn = getConn(); pstmt = conn.prepareStatement(sql);
pstmt.execute();
conn.commit();
}catch(Exception oex){ System.out.println(" has failure: "+oex);
try{
conn.rollback();
}catch(Exception ex){ ex.printStackTrace();
}
}finally{ try{ pstmt.close(); conn.close();
}catch(Exception ex){ ex.printStackTrace(); }
}
} public static Connection getConn(){ Connection conn = null;
String user = "";
String pwd = "";
String url = "jdbc:oracle:thin:@ip:sid"; t
ry{ Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection(url,user,pwd);
conn.setAutoCommit(false);
System.out.println(" OPEN DATABASE SUCCESS ");
}catch(SQLException ex){ System.out.println(" OPEN DATABASE FAILURE: "+ex);
}catch(Exception e){ System.out.println("JDBC CLASS LOADE FAILURE: "+e);
} return conn;
} public static void closeConn(Connection conn,PreparedStatement pstmt,ResultSet rs){ try{ rs.close();
pstmt.close();
conn.close();
}catch(Exception e){ e.printStackTrace();
}
}
}
create or replace package TestJDBC is -- Author
2.2创建PL/SQL包
create or replace package TestJDBC is
-- Author : JARWANG
-- Created : 2008-6-12 10:24:51
-- Purpose : TestJDBC
procedure insert_table(t varchar2);
function query_table return varchar2;
end TestJDBC;
create or replace package body TestJDBC is
procedure insert_table(t varchar2) is
language java name 'TestJDBC.insertTable(java.lang.String)';
function query_table return varchar2 is language java name 'TestJDBC.queryTable() return String';
end TestJDBC;
2.3测试调用
declare t varchar2(3000);
begin TestJDBC.insert_table('JDBC例子');
t := TestJDBC.query_table();
dbms_output.put_line('t='||t);
declare t varchar2(3000);
end;
3.调用含有外部的jar的JAVA类
3.1在ORACLE中加载JAR包。
3.1.1上传JAVA类
上传jar包至ORACLE服务器。例:/data/test/test.jar
3.1.2注册JAVA类
CALL DBMS_JAVA.loadjava('/data/test/test.jar');
3.1.3查询是否注册成功
Select
OBJECT_NAME, OBJECT_TYPE, STATUS, SYS.DBMS_JAVA.LONGNAME(OBJECT_NAME) LONGNAME, LAST_DDL_TIME, OBJECT_ID, uo.created
from sys.user_objects uo
where object_type in
('JAVA CLASS', 'JAVA SOURCE', 'JAVA RESOURCE', 'JAVA DATA')
order by uo.created desc
PL/SQL操作EXCEL
PL/SQL操作EXCEL有多种方法,但都不理想。比较了一下,还是通过JAVA的方法比较适用。
1. 使用UTL_FILE包
declare l_file utl_file.file_type;
BEGIN
l_file :=utl_file.fopen('U_FIEL','test1.xls','w');
utl_file.put_line(l_file,'jobs表导出数据');
utl_file.fflush(l_file);
end;
说明:此方法操作文本尚可,对于EXCEL这种COM文件并不可行。网络大多数所谓的PL/SQL操作EXCEL, 也是使用cvs这种简单的格式。如果要读入稍微复杂点的EXCLE模板文件,utl_file就挂掉了。
2. 使用OLE2
declare
application OLE2.OBJ_TYPE;
workbooks OLE2.Obj_Type;
workbook OLE2.Obj_Type;
worksheets OLE2.Obj_Type;
worksheet OLE2.Obj_Type;
args OLE2.List_Type;
cell OLE2.Obj_Type;
begin
application:=OLE2.CREATE_OBJ('Excel.Application');
OLE2.Set_Property(application,'Visible','True');
workbooks := OLE2.GET_OBJ_PROPERTY(application,'Workbooks');
workbook:=OLE2.INVOKE_OBJ(workbooks,'Add');
worksheets := OLE2.GET_OBJ_PROPERTY(workbook, 'Worksheets');
worksheet:=OLE2.INVOKE_OBJ(worksheets,'Add');
args:=OLE2.CREATE_ARGLIST;
OLE2.ADD_ARG(args, 4);
OLE2.ADD_ARG(args, 2);
cell := OLE2.GET_OBJ_PROPERTY(worksheet,'CELLS',args);
OLE2.DESTROY_ARGLIST(args); OLE2.Set_Property(cell, 'Value', 'Hello Excel!');
args:=OLE2.CREATE_ARGLIST; OLE2.ADD_ARG(args, 5);
OLE2.ADD_ARG(args, 3);
cell := OLE2.GET_OBJ_PROPERTY(worksheet,'CELLS',args);
OLE2.DESTROY_ARGLIST(args);
OLE2.Set_Property(cell, 'Value', 'Hello Excel!');
exception when others then FND_MESSAGE.DEBUG('ERROR:'||SQLERRM);
end;
说明:Object Link and embed,有学过VB/VC的同学们应该很熟悉。可惜ORACLE本身并不支持OLE2,OLE2是ORACLE FORMS集成的一个OLE接口。所以限制此方法的使用范围。另外更重要的OLE2无法在B/S架构中使用,只能在传统的FORM C/S程序中使用。
3. 使用ORACLE COM函数 这种方法就与通过extproc来实现与excle通信,这种方法直接与window api打交道。大致的的代码是:ordcom.CreateObject(‘Excel.Application‘, 0, servername,applicationToken); 测试了一下,似乎只能要求ORACLE的宿主系统必须是window。这也就有严重的局限性了。
4. 使用JAVA存储过程(通过POI包,这也是本文的重点)
4.1至http://poi.apache.org 下载POI包,注意与ORACLE JVM版本匹配(旧版本的POI下载:
http://archive.apache.org/dist/jakarta/poi/release/bin/)。至ORACL官网下载JDBC驱动,这一步不是必须的,一般安装ORACLE时,都有内置了JDBC包。
4.2 在ORACLE注册POI。
4.2.1先将POI包上传至ORACLE服务器。目录为:$ORACLE_HOME/javavm/lib ,例:/data/book/bookdb/9.2.0/javavm/lib,必须是javavm/lib这个目录或其下层目录。
4.2.2使用DBMS_JAVA注册
call dbms_java.loadjava('-r -v -definer -g public /data/book/bookdb/9.2.0/javavm/lib/poi-2.5.1-final-20040804.jar');
call dbms_java.loadjava('-r -v -definer -g public /data/book/bookdb/9.2.0/javavm/lib/poi-contrib-2.5.1-final-20040804.jar');
call dbms_java.loadjava('-r -v -definer -g public /data/book/bookdb/9.2.0/javavm/lib/poi-scratchpad-2.5.1-final-20040804.jar');
注:这个问题折腾了我很久,注册JAR包有两种方法。1.使用DBMS_JAVA,这种方法要自行上传jar包到服务器上。2.是使用loadjava。这也是能google到最多的方法,但loadjava在ORACLE9i以后就不单独提供了,而是集成在JDeveloper中,而可恶的JDeveloper是有版本兼容性的问题。
4.2.3查询一下是否注册成功(注:如果注册成功,STATUS应该为VALID)。
Select OBJECT_NAME, OBJECT_TYPE, STATUS, SYS.DBMS_JAVA.LONGNAME(OBJECT_NAME) LONGNAME, LAST_DDL_TIME, OBJECT_ID, uo.created
from sys.user_objects uo
where object_type in
('JAVA CLASS', 'JAVA SOURCE', 'JAVA RESOURCE', 'JAVA DATA')
and uo.object_name like '%poi%'
4.3创建JAVA SOURCE
create or replace and compile java source named testjdbc as
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.*;
import java.io.*;
public class TestJDBC{
public static void exportExcel(){
try{
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
wb.setSheetName( 0, "SomeUnicodeName", HSSFWorkbook.ENCODING_UTF_16 );
HSSFRow r = sheet.createRow( 0 );
HSSFCell c = r.createCell( (short)0 );
c.setCellValue("X中国");
// Write the output to a file FileOutputStream fileOut = new FileOutputStream("/data/book/k.xls");
wb.write(fileOut);
fileOut.close();
}catch(Exception e){
System.out.println("poi error :"+e);
e.printStackTrace();
}
}
}
4.4创建调用的PL/SQL包
create or replace package TestJDBC is
procedure exportExcel;
procedure insert_table(t varchar2);
function query_table return varchar2;
end TestJDBC;
create or replace package body TestJDBC is
procedure exportExcel is
language java name 'TestJDBC.exportExcel()';
end TestJDBC;
4.5测试PL/SQL块
begin
testjdbc.exportExcel;
end;
5. 使用其他编程语言操作 说明:此方法完全脱离了ORACLE,已超出了ORACLE的范围,本文不讨论。
PL/SQL异常机制
Exception,一些中文教材译为例外,我认为这种翻译很失水准。Exception大多数的程序语言都这个捕获机制,但基本都是译为异常。为什么在PL/SQL中就搞成“例外”.
一般的PL程序员都这样写异常处理:
Declare ……
Begin ……
Exception
When others
then Raise_application_error(-20000,‘has an error‘||sqlerrm);
End;
ORACLE有提供两个函数来实现捕获异常信息,SQLCODE:错误代码、SQLERRM:错误描述。事实上,上述写法主要是方便,也没有根本性的错误。但PL/SQL与JAVA相似,异常捕获也是有层次的。OTHERS会捕获到ORACLE全部的异常,这对于大型数据库而言,也是会影响到效率的。
实现自定义异常。
Declare
i number:=1;
Cu_ex Exception;
Begin
insert into test_table values(2,22);
这样如果发生自定异常,就不会捕获ORACLE的全部异常了。在PL/SQL如果没有定义异常,那么就相当于JAVA中抛出异常THROWS,抛出的异常由调用该块的PL/SQL程序负责处理。 另外,异常与事务,ORACLE的书上都只说异常是不会中止事务。但拿上面那段代码去测试,就会异常发生时事务是有回滚的。那么是否说明异常不会中止事务的说法不正确的? 再测试下面这段代码,异常就不会中止事务了。
BEGIN
Declare i number:=1;
Cu_ex Exception;
Begin insert into test_table values(2,22);
If (i=1) then Raise Cu_ex;
End if;
DBMS_OUTPUT.put_line('TEST'); commit;
Exception When Cu_ex then Raise_application_error(-20000,'has a customer error'||sqlerrm);
When others then Raise_application_error(-20001,'has an error'||sqlerrm);
End;
EXCEPTION WHEN OTHERS THEN
NULL;
END;
原因:Raise_application_error(-20000,'has a customer error'||sqlerrm)这段语句会再次抛出异常,但在外层块中将这个异常捕获到。所以异常不会传回调用环境,事务也就不会由中止(注意外层块中是null)。
总结:异常一般是不会中止事务的,但如果PL/SQL块将异常抛出,则事务会回传给调用环境,由服务器自动进行事务中止操作。
PL/SQL面向对象编程
自转到ORACLE开始,就开始对ORACLE OOP有非常高的热情,于是写了一个DEMO。先从创建TYPE开始,TYPE与PACKAGE非常相似。TYPE与JAVA中的CLASS可以等同。
1、 创建TYPE规范
create or replace type hek_send_mail_obj as object( mail_host varchar2(20),
mail_port integer,
member function test_f(i number)
return varchar2 )instantiable not final;
2、创建TYPE主体
create or replace type body hek_send_mail_obj as
member function test_f(i number) return varchar2 is
begin
return 'success visit ' ||i||' ' || self.mail_host||' '||self.mail_port;
end;
end;
3、进行调用
Declare
--有多少个数据成员,在实例化TYPE时,必须都初始化,这一点跟JAVA的构造器类似,只是JAVA没有强制要求。
t hek_send_mail_obj:= hek_send_mail_obj(' 192.168.1.110',44);
begin
--调用TYPE的方法test_f dbms_output.put_line(t.test_f(1));
end;
注:上面创建的TYPE都是临时对象,随着块的结束,对象生命也结束了。下面将会说明如何创建持久对象。
4、持久化TYPE(也就是创建对象表)
create table hek_send_mail_tbj of hek_send_mail_obj;
5、在对象表中INSERT些数据
insert into hek_send_mail_tbj values('192.168.210.232',25);
insert into hek_send_mail_tbj values('192.168.210.232',80);
insert into hek_send_mail_tbj values('192.168.210.232',35);
6、进行调用
select tb.mail_host,tb.mail_port,tb.test_f(2) from hek_send_mail_tbj tb
注:①这边我们可以直接象关系表一样使用对象表,也不必进行初始化。ORACLE会自动初始化对象。 ②TYPE不能使用PACKAGE的对象等等,如:UTL_SMTP。
PL/SQL实现字符串转成数组
这个功能在JAVA中比较简单,String有提供一个split的方法实现此功能。在PL/SQL其实也不难,用substr与instr联合使用就可以实现。
function varchar2_to_array(ls varchar2) return list_table is
Type list_table is table of varchar2(200);
tl list_table := list_table(NULL);
i integer :=0;
j integer :=0;
v_ls varchar2(1000);
begin i := instr(ls,',');
if i=0 then tl(1) := ls; goto ex; else v_ls :=ls;
end if;
while i>0 loop tl(j) := substr(v_ls,1,i-1); v_ls := substr(v_ls,i+1,length(v_ls));
i := instr(v_ls,',');
tl.extend; if i=0 then j := j+1;
tl(j) := v_ls; goto ex;
end if;
end loop;
<> return tl;end varchar2_to_array;
Oracle的优化器
跟DBA在随聊时,提到一个概念CBO,觉得很新奇。追问之下,得到下面这篇文章。
Oracle的优化器有两种优化方式,即基于规则的优化方式(Rule-Based Optimization,简称为RBO)和基于代价的优化方式(Cost-Based Optimization,简称为CBO),在Oracle8及以后的版本,Oracle强列推荐用CBO的方式
RBO方式:优化器在分析SQL语句时,所遵循的是Oracle内部预定的一些规则。比如我们常见的,当一个where子句中的一列有索引时去走索引。
CBO方式:它是看语句的代价(Cost),这里的代价主要指Cpu和内存。优化器在判断是否用这种方式时,主要参照的是表及索引的统计信息。统计信息给出表的大小、有少行、每行的长度等信息。这些统计信息起初在库内是没有的,是做analyze后才出现的,很多的时侯过期统计信息会令优化器做出一个错误的执行计划,因些应及时更新这些信息。
注意:走索引不一定就是优的,比如一个表只有两行数据,一次IO就可以完成全表的检索,而此时走索引时则需要两次IO,这时全表扫描(full table scan)是最好
优化模式包括Rule、Choose、First rows、All rows四种方式:
Rule:基于规则的方式。
Choolse:默认的情况下Oracle用的便是这种方式。指的是当一个表或或索引有统计信息,则走CBO的方式,如果表或索引没统计信息,表又不是特别的小,而且相应的列有索引时,那么就走索引,走RBO的方式。
First Rows:它与Choose方式是类似的,所不同的是当一个表有统计信息时,它将是以最快的方式返回查询的最先的几行,从总体上减少了响应时间。 All Rows:也就是我们所说的Cost的方式,当一个表有统计信息时,它将以最快的方式返回表的所有的行,从总体上提高查询的吞吐量。没有统计信息则走RBO的方式。
设定选用哪种优化模式:
A.Instance级别 通过在initSID.ora文件中设定OPTIMIZER_MODE=RULE/CHOOSE/FIRST_ROWS/ALL_ROWS如果没设定OPTIMIZER_MODE参数则默认用的是Choose方式。
B.Sessions级别 通过ALTER SESSION SET OPTIMIZER_MODE=RULE/CHOOSE/FIRST_ROWS/ALL_ROWS来设定。
C.语句级别用Hint(/*+ ... */)来设定
为什么表的某个字段明明有索引,但执行计划却不走索引?
1、优化模式是all_rows的方式
2、表作过analyze,有统计信息
3、表很小,上文提到过的,Oracle的优化器认为不值得走索引。
PowerDesigner
PowerDesigner一款非常好的数据库建模工具。。如果要用UML之类面向对象的东东,可以使用Rational Rose。当然也可以考虑直接使用JDeveloper10g以上版本,UML工具在JDeveloper已经有集成了。只是在ORACLE ERP很少用到面向对象,所以首推powerdesigner。PowerDesigner功能比Oracle Designer强大多了。
1.从代码反向生成E-R视图
2.