本人由于工作關系使用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);
?>