Oracle數據庫中的blob類型解析


Oracle的Blob字段比較特殊,他比long字段的性能要好很多,可以用來保存例如圖片之類的二進制數據。

寫入Blob字段和寫入其它類型字段的方式非常不同,因為Blob自身有一個cursor,你必須使用cursor對blob進行操作,因而你在寫入Blob之前,必須獲得cursor才能進行寫入,那么如何獲得Blob的cursor呢?
 
這需要你先插入一個empty的blob,這將創建一個blob的cursor,然后你再把這個empty的blob的cursor用select查詢出來,這樣通過兩步操作,你就獲得了blob的cursor,可以真正的寫入blob數據了。
 
舉例如下:
Oracle系列:LOB大對象處理
 
主要是用來存儲大量數據的 數據庫字段,最大可以存儲4G字節的非結構化數據。 主要介紹字符類型和二進制文件類型LOB數據的存儲,單獨介紹二進制類型LOB數據的存儲。
一:Oracle中的LOB數據類型分類
   1,按存儲數據的類型分:
     ①字符類型: CLOB:存儲大量 單字節 字符數據。 NLOB:存儲定寬 多字節 字符數據。
     ②二進制類型: BLOB:存儲較大無結構的二進制數據。
     ③二進制文件類型: BFILE:將二進制文件存儲在數據庫外部的操作系統文件中。存放文件路徑。
  2,按存儲方式分:
     ①存儲在內部表空間: CLOB,NLOB和BLOB   
     ②指向外部操作系統文件: BFILE
 
二,大對象數據的錄入
   1,聲明LOB類型列
  
 /* conn scott/tiger; Create TableSpace ts5_21 DataFile 'E:\Oracle\ts5_21.dbf' Size 5m; */ 
Create Table tLob ( no Number(4), name VarChar2(10), resume CLob, photo BLob, record BFile ) 
Lob (resume,photo)Store As ( Tablespace ts5_21 Chunk 6k Disable Storage In Row );

 (Tablespace ts5_21 --指定存儲的表空間 Chunk 6k --指定數據塊大小)

  2,插入大對象列
    ①先插入普通列數據
    ②遇到大對象列時,插入空白構造函數。 字符型:empty_clob(),empty_nclob() 二進制型:empty_blob() 二進制文件類型:BFileName函數指向外部文件。 BFileName函數: BFileName(‘邏輯目錄名’,‘文件名’); 邏輯目錄名只能大寫,因為數據詞典是以大寫方式存儲。Oracle是區分大小寫的。 在創建時,無需將BFileName函數邏輯目錄指向物理路徑,使用時才做檢查二者是否關聯。 例子: Insert Into tLob Values(1,'Gene',empty_clob(),empty_blob(),bfilename('MYDIR','IMG_0210.JPG'));
    ③將邏輯目錄和物理目錄關聯。(如果是二進制文件類型) 授予 CREATE ANY DIRECTORY 權限 Grant CREATE ANY DIRECTORY TO 用戶名 WITH ADMIN OPTION; 關聯邏輯目錄和物理目錄 本地 Create Directory 邏輯目錄名 As ‘文件的物理目錄’; 網絡: Create Directory 邏輯目錄名 As ‘\\主機名(IP)\共享目錄’; 例子: Create Directory MYDIR As 'E:\Oracle';
插入例子: insert into tlob values(1,'Gene','CLOB大對象列',empty_blob(),bfilename('MYDIR','IMG_0210.JPG'));
 
三,大對象數據的讀取和操作:DBMS_LOB包 DBMS_LOB包:包含處理大對象的過程和函數
/* insert into tlob values(1,'Gene','CLOB大對象列',empty_blob(),bfilename('MYDIR','IMG_0210.JPG')); 
  insert into tlob values(2,'Jack','CLOB大對象列',empty_blob(),bfilename('MYDIR','IMG_0210.JPG'));
  insert into tlob values(3,'Mary','大對象列CLOB',empty_blob(),bfilename('MYDIR','IMG_0210.JPG')); */

1,讀取大對象數據的過程和函數

  ①:DBMS_LOB.Read():從LOB數據中讀取指定長度數據到緩沖區的過程。 DBMS_LOB.Read(LOB數據,指定長度,起始位置,存儲返回LOB類型值變量); 例子: 

Declare 
  varC clob; 
  vRStr varchar2(1000); 
  ln number(4); 
  Strt number(4); 
Begin 
  select resume into varC from tlob where no = 1; 
  ln := DBMS_LOB.GetLength(varC); 
  Strt := 1; 
  DBMS_LOB.Read(varC, ln, Strt, vRStr); 
  DBMS_output.put_line('Return: '||vRStr); 
End;

 


  ②:DBMS_LOB.SubStr():從LOB數據中提取子字符串的函數。 DBMS_LOB.SubStr(LOB數據,指定提取長度,提取起始位置): 例子: 

Declare 
  varC clob;
  vRStr varchar2(1000);
  ln number(4);
  Strt number(4);
Begin
  select resume into varC from tlob where no = 1;
  ln := 4;
  Strt := 1;
  vRStr := DBMS_LOB.SubStr(varC, ln, Strt);
  DBMS_output.put_line('結果為: '||vRStr);
End;

 
  ③:DBMS_LOB.InStr():從LOB數據中查找子字符串位置的函數。 DBMS_LOB.InStr(LOB數據, 子字符串); 例子: 

Declare 
  varC clob;
  vSubStr varchar2(1000);
  vRStr varchar2(1000);
  ln number(4);
Begin
  select resume into varC from tlob where no = 1;
  vSubStr := '大對象';
  ln := DBMS_LOB.InStr(varC,vSubStr);
  DBMS_output.put_line('位置為: '||ln);   vRStr := DBMS_LOB.SubStr(varC, Length(vSubStr), ln); 
  DBMS_output.put_line('位置為'||ln||'長度為'||Length(vSubStr)||'的子字符串為:'||vRStr); 
End;

 
  ④:DBMS_LOB.GetLength():返回指定LOB數據的長度的函數。 DBMS_LOB.GetLength(LOB數據);


  ⑤:DBMS_LOB.Compare():比較二個大對象是否相等。返回數值0為相等,-1為不相等。 DBMS_LOB.Compare(LOB數據,LOB數據); 例子: 

Declare 
  varC1 clob;
  varC2 clob;
  varC3 clob;
  ln number(4);
Begin
  select resume into varC1 from tlob where no = 1;
  select resume into varC2 from tlob where no = 2;
  select resume into varC3 from tlob where no = 3;
  ln := DBMS_LOB.Compare(varC1,varC1);
  DBMS_output.put_line('比較的結果為: '||ln);
  ln := DBMS_LOB.Compare(varC2,varC3);
  DBMS_output.put_line('比較的結果為: '||ln);
End;

 

2,操作大對象數據的過程 操作會改變數據庫中原有數據,需要加上Updata鎖鎖上指定數據列,修改完后提交事務。
  ①:DBMS_LOB.Write():將指定數量的數據寫入LOB的過程。 DBMS_LOB.Write(被寫入LOB, 寫入長度(指寫入LOB數據),寫入起始位置(指被寫入LOB),寫入LOB數據); 例子: 

Declare 
  varC clob;
  vWStr varchar2(1000);
  vStrt number(4);
  ln number(4);
Begin
  vWStr := 'CLOB';
  ln := Length(vWStr);
  vStrt := 5;
  select resume into varC from tlob where no = 1 FOR UPDATE; DBMS_LOB.Write(varC, ln, vStrt, vWStr);
  DBMS_output.put_line('改寫結果為: '||varC);
  Commit;
End;

   ②:DBMS_LOB.Append():將指定的LOB數據追加到指定的LOB數據后的過程。 DBMS_LOB.Append(LOB數據,LOB數據); 例子: 

Declare 
  varC clob;
  vAStr varchar2(1000);
Begin
  vAStr := ',這是大對象列';
  select resume into varC from tlob where no = 1 FOR UPDATE;
  DBMS_LOB.Append(varC, vAStr); commit;
  DBMS_output.put_line('追加結果為: '||varC);
End;

   ③:DBMS_LOB.Erase():刪除LOB數據中指定位置的部分數據的過程; DBMS_LOB.Erase(LOB數據,指定刪除長度, 開始刪除位置); 例子: 

Declare 
  varC clob;
  ln number(4);
  strt number(4);
Begin
  ln := 1;
  strt := 5;
  select resume into varC from tlob where no = 1 FOR UPDATE;
  DBMS_LOB.Erase(varC, ln, strt);
  commit;
  DBMS_output.put_line('擦除結果為: '||varC);
End;

 
  ④:DBMS_LOB.Trim():截斷LOB數據中從第一位置開始指定長度的部分數據的過程; DBMS_LOB.Trim(LOB數據,截斷長度); 例子: 

Declare 
  varC clob;
  ln number(4);
Begin
  ln := 4;
  select resume into varC from tlob where no = 1 FOR UPDATE;
  DBMS_LOB.Trim(varC, ln);
  COMMIT; DBMS_output.put_line('截斷結果為: '||varC);
End;

 


  ⑤:DBMS_LOB.Copy():從指定位置開始將源LOB復制到目標LOB; DBMS_LOB.Copy(源LOB,目標LOB,復制源LOB長度,復制到目標LOB開始位置,復制源LOB開始位置) 例子:

Declare 
  vDEST_LOB clob; vSRC_LOB clob;
  AMOUNT number; DEST_OFFSET number;
  SRC_OFFSET number;
Begin
  select resume into vDEST_LOB from tlob where no = 1 FOR UPDATE;
  select resume into vSRC_LOB from tlob where no = 2 ;
  AMOUNT := DBMS_LOB.GetLength(vSRC_LOB);
  DEST_OFFSET := DBMS_LOB.GetLength(vDEST_LOB)+1;
  SRC_OFFSET := 1;   DBMS_LOB.Copy(vDEST_LOB, vSRC_LOB, AMOUNT, DEST_OFFSET, SRC_OFFSET);
  DBMS_output.put_line('拷貝結果為: '||vDEST_LOB);
End;

 

關於使用LOB數據類型的性能問題:
Clob/blob實現是比較復雜的,這里只提提幾個和性能相關的點,當然能不用lob盡量不用:
  a、 一個lob字段包括lobindex和lobsegment
  b、 Lob缺省可以存放在表中(表字段),條件是:
    1.它的大小小於4kb
    2.並且在定義的時候沒有使用(disable storage inrow)字句(缺省是enable)
      當lob大於4kb的時候它會被存放到lobsegment中
  c、 當lob存放在表中的時候,它可以被緩存,對於它的操作效率遠遠高於存儲在lobsegment中的lob(不用lobindex)
  d、 存儲在lobsegment中的lob缺省不在緩沖區緩存,對於lob的讀寫都是物理IO,代價非常高,所以對於大於4kb的lob字段千萬不要頻繁更新,效率非常低
  e、 存儲在lobsegment中的lob可以在定義的時候指定使用cache(默認是nocache),這對於中等大小的lob(比如幾k~幾十k)很有效果,減少物理IO
 
使用其他數據類型需要考慮的性能問題:
1、 Char 定長格式字符串,在數據庫中存儲時不足位數填補空格,不建議使用,會帶來不必要的麻煩
  a、 字符串比較的時候,如果不注意(char不足位補空格)會帶來錯誤
  b、 字符串比較的時候,如果用trim函數,這樣該字段上的索引就失效(有時候會帶來嚴重性能問題)
  c、 浪費存儲空間

2、 Varchar2/Varchar 不定長格式字符串,對於4000字節以內的字符串,建議都用該類型
  a、 網上有說char比varchar2性能好,但是如果你有興趣做測試的話,會發現沒有區別(如果發生行遷移,可以通過pctfree來調整)
  b、 充分利用存儲空間

3、 Long/long raw Oracle已經廢棄,只是為了向下兼容保留着,應該全部升級到lob Long類型有很多限制
  a、 表中只能有一列long類型
  b、 Long類型不支持分布式事務
  c、 太多的查詢不能在long上使用了

4、 Number 定義Number的方法:
  Number(p,s) 其中p,s都是可選的: a、 p代表精度,默認為38       b、 s代表小數位數,取值范圍-84~127,默認取值要看是否指定了p,如果制定了p,默認s為0,如果沒有指定p,默認取最大值。
   幾個例子:
  a、 Number(5,0)=Number(5) 取值范圍99999~-99999
  b、 Number(5,2) 取值范圍999.99~-999.99 注意:其中的整數位數只有3位,小數位數有2位,按照如下方法計算: 整數位數<=p-s 小數位數<=s 如果插入123.555存儲在數據庫中變成123.56 (在小數的第三位上四舍五入),如果插入999.999,數據庫就要拋錯。
  c、 Number(5,-2) 取值范圍9999900~-9999900 (整數位數<=p-s,沒有小數位數) 如果插入9999949存儲在數據庫中變成9999900(在整數的第二位上四舍五入),如果插入9999950,數據庫就要拋錯。 其他的數值類型都是number的衍生,底層都是number,比如integer/int完全映射到number(38) 性能相關:number是一種軟實現的類型,如果需要對number做復雜的運算,建議先用cast內置函數轉換number為浮點數類型 另外需要注意的一點是: number是變長類型,在計算表存儲空間的時候要切記

5、 Date Date類型是一個7字節的定長數據類型,沒啥好說的,一個例子:
  性能a>b>c
   a、Where date_colum>=to_date(’01-jan-2007’,’dd-mon-yyyy’) and date_colum< DIV>
   b、Where trunc(date_colum,’y’)=to_date(’01-jan-2007’,’dd-mon-yyyy’)
   c、Where to_char(date_colum,’yyyy’)=’2007’

6、 Timestamp/timestamp with time zone/timestamp with local time zone 和date類似,只不過它另外支持小數秒和時區。語法Timestamp(n),n指定秒的小數位數,取值范圍0~9。可選


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM