本人由于工作关系使用oracle数据库,发现这里用的人不多,但时常发现有人提的关于php操作oracle数据库的问题得不到回答,我也曾问过几个,但也无人响应,因此决定把本人在工作中积攒起来的一些技巧、经验奉献出来,希望对使用oracle数据库的人有所帮助。
一、配置环境:
访问oracle8以上的数据库需要用到oracle8 call-interface(oci8)。这个扩展模块需要oracle8 的客户端函数库,因此需要你要连接远程oracle数据库的话,还要装上oracle的客户端软件-可以到oracle网站上免费下载- http://www.oracle.com,这是许多初学者常常忽略的,因此如果看了这篇文章,就不要在论坛上再提“为什么我连接不上oracle数据库”之类的问题了。
(1)首先确认安装了oracle8i客户端,然后用net8 assistant(客户端软件提供)建立一个服务命名,注意服
务名是oracle数据库的sid,可查询initsid文件里的server_names得到。
(2)在php.ini中把 ;extension=php_oci8.dll 前面的注释符号“;”去掉,使php能够加载支持oracle的模块
。并把php_oci8.dll拷贝到你的windows2000 server安装目录下的system32子目录。如d:/winnt/system32,重
新启动你的机器。
(3)写个测试文件试一下是否能正确连接(假如服务名sid是test):
这里scott用户是oracle自带的无须你自己建立了,只要把下面这个文件放到你的web根目录就可以了。如果显
示到数据库中的数据,则说明连接正常,如果不行,你还要检查前几步有哪些地方做错了。
test.php
<?
$dbconn=ocilogon("scott","tiger","test");
$sql ="select * from emp";
$stmt = ociparse($dbconn, $sql);
if(!$stmt) {
echo "<h1>error - could not parse sql statement.</h1>";
exit;
}
ociexecute($stmt);
while( ocifetchinto($stmt, &$result_array) )
{
echo
"empno=$result_array[0];ename=$result_array[1];job=$result_array[2];mgr=$result_array[3]<br>";
}
?>
二、用php执行oracle存储过程
(1)用sqlplus连接后,建立一个存储过程:
create or replace procedure inoutdemo (
par_in in varchar2,
par_in_out in out varchar2,
par_out out varchar2)
is
begin
par_out := par_in;
par_in_out := par_in || ' ' || par_in_out;
end;
(2)php文件:
sptest.php
<?
//:in是输入变量;:inout是输入输出变量;:out是输出变量,具体解释请参考oracle的pl/sql手册
$conn=ocilogon("scott","tiger","test");
$stmt = ociparse($conn,"begin inoutdemo(:in,:inout,:out); end;");
ocibindbyname($stmt,":in",$in,32);
ocibindbyname($stmt,":inout",$inout,32);
ocibindbyname($stmt,":out",$out,32);
$in = "hello ";
$inout = "world!";
ociexecute($stmt);
echo "<br><br>";
echo "in=".$in."<br>";
echo "inout=".$inout."<br>";
echo "out=".$out."<br>";
?>
三、oracle数据库的分页
oracle虽然不象mysql有limit可用,十分方便,但也有自己的处理方法,它特殊的rownum对分页有很重要的作
用。分页可有很多种方法,其中最常用的是用minus。
如要显示n1-n2记录可写为:
(1)select * from tablename where rownum <= n2 minus select * from tablename where rownum < n1
注意:该语句不能使用order by,否则报错。
(2)把指针下移的办法如:
其中:$page是当前页;$pagesize是每页显示的记录数
for($i=0;$i<($page-1)*$pagesize;$i++)
{
@ocifetch($stmt);
}
然后再用ocifetch($stmt)取出的数据就是你要显示的记录了
(3)对于有复杂查询语句并用order by来排序的,可使用下面方法解决:
select table_name,table_type from( select rownum rowseq,x.* from (select * from cat order by
table_type) x) where rowseq between n1+1 and n2;
本人最喜欢的是第三种,也推荐大家使用,非常方便的啊。呵呵。
其它方法就不介绍了,很麻烦,使用了oracle游标之类的东东,不太适合php使用。
四、特殊字符的插入处理
对于一些字符如单引号'在oracle里是不能用addslashes处理的,但可以使用oracle的chr函数或再加个单引号
。
如:sql>insert into table values('it'||chr(39)||'s a test'));
或 sql>insert into table values('it''s a test'));
显示:
it's a test.
五、php和oracle的事务处理
ociexecute()函数:int ociexecute ( int statement [, int mode] )
第二个参数mode共有两个:缺省为oci_commit_on_success,可省略。oci_default 表示用事务(transation)
提交,不自动提交。
如果你在程序中如果有两个操作数据库的语句需要同时成功执行,有一个失败就要rollback的话,可这样写:
$conn=ocilogon($username,$password,$sid);
//first sentence
$sql = "insert into tablename values()";
$stmt=ociparse($conn,$sql);
$result=ociexecute($stmt, oci_default);
if (!$result) {
ocirollback($conn);//不成功则回滚
ocifreestatement($stmt); //释放资源
ocilogoff($conn);
}
//second sentence
$sql = " update tablename set..";
$stmt=ociparse($conn,$sql);
$result=ociexecute($stmt, oci_default);
if (!$result) {
ocirollback($conn);//不成功则回滚
ocifreestatement($stmt); //释放资源
ocilogoff($conn);
}
ocicommit($conn);//如果都成功则提交
ocifreestatement($stmt); //释放资源
ocilogoff($conn);
六、用php操纵oracle的lob类型的数据(含图片的存储与显示处理)
对php程序员来讲,oracle最令人头痛的莫过于使用lob来处理图片了。
1。php操作blob:
先建立一个表用于保存图片。用户上传的图片文件存放到blob中
create table pictures (
id number,
imgtype, varchar2(60),
description varchar2(100),
picture blob
);
如果要实现id的自动增加,再建一个sequence:
create sequence pic_seq;
php程序-插入部分:
<?
$conn=ocilogon($username,$password,$sid);
//在这里要注意的两点:一是用empty_blob()函数。这是oracle的内部函
//数,返回一个lob的定位符。在插入lob时,只能用这个办法先生成一个
//空的lob定位符,然后对这个定位符进行操作。empty_blob()函数是针
//对blob类型的,对应于clob的是empty_clob()。二是returning后面的
//部分,把picture返回,让php的oci函数能够处理。
$stmt = ociparse($conn,"insert into pictures (id, imgtype,description, picture) values
(pic_seq.nextval, '$imgtype','$description', '$lob_upload_type', empty_blob()) returning picture
into :picture");
//生成一个本地lob对象的描述符。注意函数的第二个参数:oci_d_lob,
//表示生成一个lob对象。其它可能的还有oci_d_file和oci_d_rowid,分
//别对应于bfile和rowid对象。
$lob = ocinewdescriptor($conn, oci_d_lob);
//将生成的lob对象绑定到前面sql语句返回的定位符上。
ocibindbyname($stmt, ':picture', &$lob, -1, oci_b_blob);
ociexecute($stmt);
//方法一:向lob对象中存入数据。因为这里的源数据是一个文件,所以直接用lob对象的savefile()方法。lob
对象的其它方法还有:save()和load(),分别用来保存和取出数据。但bfile类型只有一个方法就是save()
if($lob-〉savefile($lob_upload)){
ocicommit($conn);
echo "上传成功〈br〉";
}else{
echo "上传失败〈br〉";
}
//方法二:用save的方法保存
//$fp = fopen($lob_upload, "r");
//$file->save(fread($fp, filesize($lob_upload)));
//fclose($fp );
//释放lob对象
ocifreedesc($lob);
ocifreestatement($stmt);
ocilogoff($conn);
?>
小技巧:在sqlplus里可用select dbms_lob.getlength(picture) from pictures;查看文件是否已存入到数据
库或在php程序里用strlen()函数查看。
php程序-显示部分(getpicture.php):
<?
$conn = ocilogon($username, $password, $sid);
$stmt = ociparse($conn,"select imgtype,picture from pictures where id=$pictureid");
if (ocifetchinto($stmt, $result))
{
header("content-type: ".$result[0]);
echo $result[1]->load();
}
//可用strlen($result[1]->load()) 查看图片的大小以确定图片是否正确存入到数据库。
?>
在需要显示图片的地方只要:
<img src="getpicture.php?pictureid=99" alt="放在oracle lob中的图片">
就能显示图片了
有的网上文章写用返回lob值而非描述符的方法显示,我没有试成功,大家可以试下
代码如下:
if (ocifetchinto($stmt, $result, oci_assoc+oci_return_lobs))
{
echo "content-type: " . stripslashes($result[imgtype]);
echo stripslashes($result[picture]);
}
2。php操作clob:
oracle有一种数据类型叫varchar2,用来表示不定长的字符串。varchar2也是oracle公司推荐使用的类型。但
使用varchar2有个问题:最大只能表示4000个字符,也就相当于2000个汉字。如果你的程序中某个字符串的长
度要大于2000个汉字,用varchar2就不能满足要求了。这时候,你可以尝试使用clob。clob和blob的最大长度
是4gb。
下面是示例(参考了php英文版的手册):
<?
//要保存的文字
$clobtext="different dr2";
//db connection
$conn = ocilogon("user","pw","tns");
//这里原例子使用了一个存储过程,你也可以用上面操作blob的方法来实现。
//如:$stmt = ociparse($conn,"insert into table (id, clobtext) values (text.nextval,,
empty_clob()) returning clobtext into :clob");
$sql = "begin tempclobtest_package.saveclob(:clob); end;";
$clob = ocinewdescriptor($conn, oci_d_lob);
$stmt = ociparse($conn, $sql);
ocibindbyname ($stmt,':clob', &$clob , -1,oci_b_clob );
if(!ociexecute($stmt, oci_default)) {print_r(ocierror($stmt));}
else{echo "提交成功";}
if($clob->save($clobtext))
{
ocicommit($conn);
echo "提交成功";
}
else
{
print_r(ocierror($stmt));
}
//释放资源
$clob->free();
ocifreestatement($stmt);
?>