处理Clob数据(转)关于oracle中大对象处理的一些方法和实例

来自CSDN
=======================================
在oracle中,有4个大对象(lobs)类型可用,分别是blob,clob,bfile,nclob。  
下面是对lob数据类型的简单介绍。  
l blob:二进制lob,为二进制数据,最长可达4GB,存贮在数据库中。  
l clob:字符lob,字符数据,最长可以达到4GB,存贮在数据库中。  
l bfile:二进制文件;存贮在数据库之外的只读型二进制数据,最大长度由操作系统限制。  
l nclob:支持对字节字符集合(nultibyte characterset)的一个clob列。  
对于如何检索和操作这些lob数据一直是oracle数据库开发者经常碰到的问题。下面我将在oracle对lob数据处理的一些方法和技巧,介绍给读者,希望能够对读者以后的开发有所帮助。  
oracle中可以用多种方法来检索或操作lob数据。通常的处理方法是通过dbms_lob包。  
其他的方法包括使用api(application programming interfaces)应用程序接口和oci(oracle call interface)oracle调用接口程序。  
一、在oracle开发环境中我们可以用dbms_lob包来处理!dbms_lob包功能强大,简单应用。既可以用来读取内部的lob对象,也可以用来处理bfile对象。但处理两者之间,还有一点差别。处理内部lob对象(blob,clob)时,可以进行读和写,但处理外部lob对象bfile时,只能进行读操作,写的操作可以用pl/sql处理。另外用sql也可以处理lob,但要注意sql仅可以处理整个lob,不能操作lob的数据片。  
在dbms_lob包中内建了read(),append,write(),erase(),copy(),getlength(),substr()等函数,可以很方便地操作lob对象。这里不做深入讨论,读者可以参看相关的书籍。  
对于pl/sql,下面介绍一种技巧,用动态的pl/sql语句处理clob对象来传替表名!  
example 1.  
动态PL/SQL,对CLOB字段操作可传递表名table_name,表的唯一标志字段名field_id,clob字段名field_name记录号v_id,开始处理字符的位置v_pos,传入的字符串变量v_clob   
修改CLOB的PL/SQL过程:updateclob   
create or replace procedure updateclob(  
table_name in varchar2,  
field_id in varchar2,   
field_name in varchar2,  
v_id in number,  
v_pos in number,  
v_clob in varchar2)  
is  
lobloc clob;  
c_clob varchar2(32767);  
amt binary_integer;  
pos binary_integer;  
query_str varchar2(1000);  
begin  
pos:=v_pos*32766+1;  
amt := length(v_clob);  
c_clob:=v_clob;  
query_str :='select '||field_name||' from '||table_name||' where '||field_id||'= :id for update ';  
--initialize buffer with data to be inserted or updated  
EXECUTE IMMEDIATE query_str INTO lobloc USING v_id;  
--from pos position, write 32766 varchar2 into lobloc  
dbms_lob.write(lobloc, amt, pos, c_clob);  
commit;  
exception  
when others then  
rollback;  
end;  
l /用法说明:   
在插入或修改以前,先把其它字段插入或修改,CLOB字段设置为空empty_clob(),   
然后调用以上的过程插入大于2048到32766个字符。   
如果需要插入大于32767个字符,编一个循环即可解决问题。   
查询CLOB的PL/SQL函数:getclob   
create or replace function getclob(  
table_name in varchar2,  
field_id in varchar2,   
field_name in varchar2,  
v_id in number,  
v_pos in number) return varchar2  
is  
lobloc clob;  
buffer varchar2(32767);  
amount number := 2000;  
offset number := 1;  
query_str varchar2(1000);  
begin  
query_str :='select '||field_name||' from '||table_name||' where '||field_id||'= :id ';  
--initialize buffer with data to be found  
EXECUTE IMMEDIATE query_str INTO lobloc USING v_id;  
offset:=offset+(v_pos-1)*2000;   
--read 2000 varchar2 from the buffer  
dbms_lob.read(lobloc,amount,offset,buffer);  
return buffer;  
exception  
when no_data_found then  
return buffer;  
end;  
l 用法说明:   
用select getclob(table_name,field_id,field_name,v_id,v_pos) as partstr from dual;   
可以从CLOB字段中取2000个字符到partstr中,   
编一个循环可以把partstr组合成dbms_lob.getlength(field_name)长度的目标字符串。   
二、对于在其他不同的开发环境,例如vc,vb,pb,java等环境下对lob的处理,处理方法不尽相同,在这里将简要举几个例子来说明不在oracle开发环境下对lob的处理。  
(一) 在pb中的处理  
exampler 2.  
string ls_path,ls_filename,ls_jhdh   
long ll_num,ll_count,rtn   
blob ole_blob   
ll_num=dw_lb.getrow()   
if ll_num>0 then ls_jhdh=dw_lb.object.ct_njhdh[ll_num]   
select count(*) into :ll_count from sj_jh_jhfjb where ct_jhdlxbh='1' and ct_jhdh=:ls_jhdh and ct_jdlxbh=:is_jdlx;   
if ll_count>0 then   
rtn=messagebox("提示","是否要修改此附件",question!,yesno!,1)   
if rtn=1 then   
SELECTBLOB ct_jhfjnr INTO le_blob from sj_jh_jhfjb where ct_jhdlxbh='1' and ct_jhdh=:ls_jhdh and ct_jdlxbh=:is_jdlx;   
ole_1.objectdata =ole_blob   
If ole_1.activate(offsite!) <> 0 Then   
Messagebox("OLE Activate","不能激活")   
Return -1   
end If   
end if   
else   
messagebox("提示","没有附件")   
end if   
end if  
(二)在vb中的处理  
在vb中处理大对象,一般可以用OO4O(oracle objects for ole)来处理大对象。这里介绍一种不用0040处理大对象blob的方法。  
下面这段程序可以将一个文件(文本文件,doc文件,图象文件等)保存到数据库中,并可以将其从数据库读出   
需要两个commandbutton   
cmd1 名称 cmdsave caption 保存   
cmd2 名称 cmdread caption 读取   
一个cmddialog控件   
同时需要创建一张表t_demo(字段id 类型 number,;字段text 类型 blob;)  
exmple 3.  
Option Explicit  
Dim rn As ADODB.Connection  
Public Function CreateDataSource(DataSource As String, UserID As String, Password As String) As Boolean  
On Error GoTo DbConErr:  
Set rn = New ADODB.Connection  
With rn  
.ConnectionString = "Provider=OraOledb.Oracle.1;" & _  
"password=" [$ Password & "]" & _  
"User ID =" [$ UserID & "]" & _  
"Data Source=" [$ DataSource & "]" & _  
"Locale Identifier=2052"  
.Open  
End With  
CreateDataSource = True  
Exit Function  
DbConErr:  
CreateDataSource = False  
End Function  
  
Private Sub cmdRead_Click()  
Dim rs As New ADODB.Recordset  
rs.ActiveConnection = rn  
rs.LockType = adLockOptimistic  
rs.CursorLocation = adUseClient  
rs.Source = "select * from t_demo"  
rs.Open  
ComDlgDir.DialogTitle = "保存文件"  
ComDlgDir.Filter = "*.*"  
ComDlgDir.ShowSave  
Call BlobToFile(rs.Fields("text"), ComDlgDir.filename)  
Set rs = Nothing  
Exit Sub  
Set rs = Nothing  
End Sub  
  
Private Sub cmdsave_Click()  
Dim rs As New ADODB.Recordset  
rs.ActiveConnection = rn  
rs.LockType = adLockOptimistic  
rs.CursorLocation = adUseClient  
rs.Source = "select * from t_demo"  
rs.Open  
rs.AddNew   
ComDlgDir.DialogTitle = "选取文件"  
ComDlgDir.ShowOpen   
rs.Fields("id").Value = 1  
If ComDlgDir.filename <> "" Then  
Call FileToBlob(rs.Fields("text"), ComDlgDir.filename)  
rs.Update  
End If   
Set rs = Nothing  
Exit Sub  
Set rs = Nothing   
End Sub  
  
Private Sub Form_Load()  
If Not CreateDataSource("sid", "systemp", "manager") Then  
MsgBox "Connection failure!"  
End If  
End Sub  
  
fld As ADODB.Field, filename As String, Optional ChunkSize As Long = 8192)  
Dim fnum As Integer, bytesleft As Long, bytes As Long  
Dim tmp() As Byte  
If (fld.Attributes And adFldLong) = 0 Then  
Err.Raise 1001, , "field doesn't support the GetChunk method."  
End If  
If Dir$(filename) = "" Then Err.Raise 53, , "File not found"  
fnum = FreeFile   
Open filename For Binary As fnum  
bytesleft = LOF(fnum)  
Do While bytesleft  
bytes = bytesleft  
If bytes > ChunkSize Then bytes = ChunkSize  
ReDim tmp(1 To bytes) As Byte  
Get fnum, , tmp  
fld.AppendChunk tmp  
bytesleft = bytesleft - bytes  
Loop  
Close #fnum  
End Sub  
  
Sub BlobToFile(fld As ADODB.Field, filename As String, Optional ChunkSize As Long = 8192)  
Dim fnum As Integer, bytesleft As Long, bytes As Long  
Dim tmp() As Byte  
If (fld.Attributes And adFldLong) = 0 Then  
Err.Raise 1001, , "field doesn't support the GetChunk method."  
End If  
If Dir$(filename) <> "" Then Kill filename  
fnum = FreeFile  
Open filename For Binary As fnum  
bytesleft = fld.ActualSize  
Do While bytesleft  
bytes = bytesleft  
If bytes > ChunkSize Then bytes = ChunkSize  
tmp = fld.GetChunk(bytes)  
Put #fnum, , tmp  
bytesleft = bytesleft - bytes  
Loop  
Close #fnum  
End Sub

(三)用jdbc处理lob  
exmple 4.  
首先是Getting BLOB and CLOB Locators from a Result Set  
// Select LOB locator into standard result set.  
ResultSet rs =stmt.executeQuery ("SELECT blob_col, clob_col FROM lob_table");  
while (rs.next())  
{// Get LOB locators into Java wrapper classes.  
oracle.jdbc2.Blob blob = (oracle.jdbc2.Blob)rs.getObject(1);  
oracle.jdbc2.Clob clob = (oracle.jdbc2.Clob)rs.getObject(2);  
[...process...]  
}  
然后是Read BLOB data from BLOB locator.  
InputStream byte_stream = my_blob.getBinaryStream();  
byte [] byte_array = new byte [10];  
int bytes_read = byte_stream.read(byte_array);  
和Writing BLOB Data   
java.io.OutputStream outstream;  
// read data into a byte array   
byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};  
// write the array of binary data to a BLOB  
outstream = ((BLOB)my_blob).getBinaryOutputStream();  
outstream.write(data);  
还有Passing a BLOB Locator to a Prepared Statement  
OraclePreparedStatement ops = (OraclePreparedStatement)conn.prepareStatement  
"INSERT INTO blob_table VALUES(?)");   
ops.setBLOB(1, my_blob);  
ops.execute();  
最后应该注意:  
insert的时候一定要用empty_blob()初始化  
stmt.execute ("insert into my_blob_table values ('row1', empty_blob()");  
  
(四)在pro*c中的处理  
PRO*C可以用三种方式对LOB字段处理。   
1、The DBMS_LOB package inside PL/SQL blocks.   
2、OCI (Oracle Call Interface) function calls.   
3、Embedded SQL statements.   
Embedded SQL statements.的方式简单而且比较灵活。OTN上提供一个例子:   
In this example we will be reading data from a BLOB with an unknown arbitrary length into a buffer and then writing the data from the buffer into an external file.   
Our buffer is small, so depending on the size of the BLOB we are reading, we may   
be able to read the BLOB value into the buffer in a single READ statement or we   
may be required to utilize a standard polling method instead.   
First we start off with oci.h and some simple local variable declarations   
example 5.  
#include <oci.h>   
OCIBlobLocator *blob ;   
FILE *fp ;   
unsigned int amt, offset = 1 ;   
Now we need a buffer to store the BLOB value and then write to the file from:   
#define MAXBUFLEN 5000   
unsigned char buffer[MAXBUFLEN] ;   
EXEC SQL VAR buffer IS RAW(MAXBUFLEN) ;   
Allocate the BLOB host variable and select a BLOB which we will READ:   
EXEC SQL ALLOCATE :blob ;   
EXEC SQL SELECT a_blob INTO :blob FROM lob_table WHERE ... ;   
We can then open the external file to which we will write the BLOB value:   
fp = fopen((const char *)"image.gif", (const char *)"w") ;   
If the buffer can hold the entire LOB value in a single READ we need to catch the   
NOT FOUND condition to signal LOB READ termination:   
EXEC SQL WHENEVER NOT FOUND GOTO end_of_lob ;   
Now do our first READ.We set the amount to the maximum value of 4 Gigabytes. It   
is larger than our buffer so if the LOB doesn't fit we will READ using a polling   
mode:   
amt = 4294967295 ;   
EXEC SQL LOB READ :amt FROM :blob AT ffset INTO :buffer ;   
If we get here then it means that the buffer was not large enough to hold the entire   
LOB value, so we must write what we have using binary I/O and continue reading:   
(void) fwrite((void *)buffer, (size_t)MAXBUFLEN, (size_t)1, fp) ;   
We use a standard polling method to continue reading with the LOB READ inside   
of an infinite loop. We can set up the NOT FOUND condition to terminate the loop:   
EXEC SQL WHENEVER NOT FOUND DO break ;   
while (TRUE)   
{   
During polling, the offset is not used so we can omit it in subsequent LOB READs.   
We need the amount, however, because it will tell us how much was READ in the   
last READ invocation   
EXEC SQL LOB READ :amt FROM :blob INTO :buffer ;   
(void) fwrite((void *)buffer, (size_t)MAXBUFLEN, (size_t)1, fp) ;   
}   
Here, we have reached the end of the LOB value. The amount holds the amount of   
the last piece that was READ. During polling, the amount for each interim piece   
was set to MAXBUFLEN, or the maximum size of our buffer:   
end_of_lob:   
(void) fwrite((void *)buffer, (size_t)amt, (size_t)1, fp) ;   
  
(五) 在delphi中的处理  
对于lob字段而言,个人认为其使用比long类型有很大的灵活性,而且lob字段可以保存各类的数据,可以保存图片,大量的文字,现就clob跟blob两种类型加以说明,其中blob保存图片信息,clob保存大量文字。  
exmple 6.  
Create table test_table  
(c_no number(1) not null,  
c_blob blob,  
c_clob clob,  
constraint pk_test_table primary key (c_no));  
  
unit Unit1;  
  
interface  
  
uses  
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  
Dialogs, StdCtrls, DBCtrls, Grids, DBGrids, DB, DBTables, ExtDlgs;  
  
type  
TForm1 = class(TForm)  
Database1: TDatabase; //用于连接数据库  
Table1: TTable; //获取表信息  
DataSource1: TDataSource;   
DBGrid1: TDBGrid;  
DBMemo1: TDBMemo; //显示c_clob字段内容  
DBImage1: TDBImage; //显示c_blob字段内容  
Button1: TButton; //插入按钮  
Button2: TButton; //保存按钮  
Table1C_NO: TFloatField; //Tfiled  
Table1C_BLOB: TBlobField;  
Table1C_CLOB: TMemoField;  
OpenPictureDialog1: TOpenPictureDialog; //从文件获取图片  
OpenDialog1: TOpenDialog; //从文件获取文字  
procedure Button1Click(Sender: TObject);  
procedure Button2Click(Sender: TObject);  
private  
{ Private declarations }  
public  
{ Public declarations }  
end;  
  
var  
Form1: TForm1;  
  
implementation  
  
{$R *.dfm}  
  
procedure TForm1.Button1Click(Sender: TObject);  
begin //插入操作  
with Table1 do  
begin  
Insert; //将表状态置为插入状态  
if OpenPictureDialog1.Execute then //获得图片信息  
Table1C_BLOB.LoadFromFile(OpenPictureDialog1.FileName);  
if OpenDialog1.Execute then //获得文字信息  
Table1C_CLOB.LoadFromFile(OpenDialog1.FileName);  
end;  
end;  
  
procedure TForm1.Button2Click(Sender: TObject);  
begin //提交插入内容  
try  
Table1.Post;  
except  
Application.MessageBox('错误发生','警告',0);  
end;  
end;  
  
end.  
  
注意:  
openpiceturedilog只能打开dmp,ico,wmf等文件,事先需要将图片文件格式保存成这几类;  
在文字字段不从文件获得时,可以手动输入  
本例只是对lob字段的一个小小的探索,用法不当及需改正之处,还请多多指教。  
  
注:本篇文章大部分例子均取自论坛,如有侵犯您的版权,请来信告知,我会做相应处理。  
  
      
   注意:  
openpiceturedilog只能打开dmp,ico,wmf等文件,事先需要将图片文件格式保存成这几类;  
在文字字段不从文件获得时,可以手动输入

posted on 2006-12-27 20:10  Sammy  阅读(1809)  评论(0编辑  收藏  举报

导航