oracle對大對象類型操作:blob,clob,nclob(轉載)

1.基本介紹

Oracle和plsql都支持lob(large object) 類型,用來存儲大數量數據,如圖像文件,聲音文件等。Oracle 9i realse2支持存儲最大爲4g的數據,oracle 10g realse1支持最大8到128萬億字節的數據存儲,依賴於你的db的block size。

在plsql中可以申明的lob類型的變量如下: 

  • BFILE        二進制文件,存儲在數據庫外的操作系統文件,只讀的。把此文件當二進制處理。 
  • BLOB        二進制大對象。存儲在數據庫裏的大對象,一般是圖像聲音等文件。 
  • CLOB        字符型大對象。一般存儲大數量文本信息。存儲單字節,固定寬度的數據。 
  • NCLOB        字節字符大對象。存儲單字節大塊,多字節固定寬度,多字節變寬度數據。

Oracle將lob分類爲兩種:

  1. 存儲在數據庫裏的,參與數據庫的事務。BLOB,CLOB,NCCLOB。

  2. 存儲在數據庫外的BFILE,不參與數據庫的事務,也就是不能rollback或commit等,它依賴於文件系統的數據完整性。
LONG和LONG
RAW這兩種數據類型也是存儲字符的,但是有系列的問題,不建議使用,這裏也就不討論了。 

2.LOB的使用 

本部分不討論lob的所有細節,只討論lob的基本原理和在plsql中的基本使用,爲plsql開發使用lob提供一個基礎性指導。 

本部分使用的表是: 
  1. /**   
  2. table script   
  3. **/   
  4. CREATE TABLE waterfalls (   
  5.        falls_name VARCHAR2(80),--name   
  6.        falls_photo BLOB,--照片   
  7.        falls_directions CLOB,--文字   
  8.        falls_description NCLOB,--文字   
  9.        falls_web_page BFILE);--指向外部的html頁面   
  10. /  
  1. /**   
  2. table script   
  3. **/   
  4. CREATE TABLE waterfalls (   
  5.        falls_name VARCHAR2(80),--name   
  6.        falls_photo BLOB,--照片   
  7.        falls_directions CLOB,--文字   
  8.        falls_description NCLOB,--文字   
  9.        falls_web_page BFILE);--指向外部的html頁面   
  10. /  
這個表我們並不需要clob和nclob兩個,只取一就可以,這裏全部定義只是爲了演示使用。

1. 理解LOB的Locator

表中的Lob類型的列中存儲的只是存儲指向數據庫中實際存儲lob數據的一個指針。 

在plsql中申明瞭一個lob類型的變量,然後從數據庫中查詢一個lob類型的值分配給變量,也只是將指針複製給了它,那麼這個變量也會指向數據庫中實際存放lob數據的地方。如:
  1. --understanding lob locators   
  2.   DECLARE   
  3.        photo BLOB;   
  4.     BEGIN   
  5.        SELECT falls_photo   
  6.          INTO photo   
  7.          FROM waterfalls   
  8.         WHERE falls_name='Dryer Hose';   
  1. --understanding lob locators   
  2.   DECLARE   
  3.        photo BLOB;   
  4.     BEGIN   
  5.        SELECT falls_photo   
  6.          INTO photo   
  7.          FROM waterfalls   
  8.         WHERE falls_name='Dryer Hose';   
Lob工作原理圖解 

       從上面的圖可以看出,要處理lob數據,必須先獲得lob locators。我們可以通過一個select語句獲取,當賦值給lob變量的時候,它也獲得同樣的lob locators。我們在plsql中處理可以使用dbms_lob包,裏面內置了很多過程和函數來讀取和修改我們的lob數據。下面給出處理lob數據的一般方法。 

1.        通過select語句獲取一個lob locator。 

2.        通過調用dbms_lob.open打開lob。 

3.        調用dbms_lob.getchunksize獲得最佳讀寫lob值。 

4.        調用dbms_lob.getlength獲取lob數據的字節值。 

5.        調用dbms_lob.read獲取lob數據。 

6.        調用dbms_lob.close關閉lob。 

2.Empty
lob and Null lob

Empty的意思是我們已經獲取了一個lob
locator,但是沒有指向任何lob數據。Null是定義了一個變量,但是沒有獲得lob locator。對lob類型的處理和其他類型不一樣。如下面的例子:
  1. /* null lob example*/  
  2. DECLARE  
  3.   directions CLOB; --定義了,但是沒有分配值,爲null   
  4. BEGIN  
  5.   IF directions IS NULL THEN  
  6.     dbms_output.put_line('directions is null');  
  7.   ELSE  
  8.     dbms_output.put_line('directions is not null');  
  9.   END IF;  
  10. END;  
  11. /  
  12. DECLARE  
  13.   directions CLOB; --定義一個,並且分配值   
  14. BEGIN  
  15.   --刪除一行   
  16.   DELETE FROM waterfalls WHERE falls_name = 'Munising Falls';  
  17.   --插入一行通過使用 EMPTY_CLOB(  ) to 建立一個lob locator   
  18.   INSERT INTO waterfalls  
  19.     (falls_name, falls_directions)  
  20.   VALUES  
  21.     ('Munising Falls', empty_clob());  
  22.   --獲得lob locator,上面插入的數據,因爲我們插入的是一個empty_clob(),那麼lob locator不指向任何數據,雖然給變量分配了只   
  23.   SELECT falls_directions  
  24.     INTO directions  
  25.     FROM waterfalls  
  26.    WHERE falls_name = 'Munising Falls';  
  27.   IF directions IS NULL THEN  
  28.     dbms_output.put_line('directions is NULL');  
  29.   ELSE  
  30.     dbms_output.put_line('directions is not NULL'); --打印此句   
  31.   END IF;  
  32.   dbms_output.put_line('Length = ' || dbms_lob.getlength(directions)); --結果爲o   
  33. END;  
  1. /* null lob example*/  
  2. DECLARE  
  3.   directions CLOB; --定義了,但是沒有分配值,爲null   
  4. BEGIN  
  5.   IF directions IS NULL THEN  
  6.     dbms_output.put_line('directions is null');  
  7.   ELSE  
  8.     dbms_output.put_line('directions is not null');  
  9.   END IF;  
  10. END;  
  11. /  
  12. DECLARE  
  13.   directions CLOB; --定義一個,並且分配值   
  14. BEGIN  
  15.   --刪除一行   
  16.   DELETE FROM waterfalls WHERE falls_name = 'Munising Falls';  
  17.   --插入一行通過使用 EMPTY_CLOB(  ) to 建立一個lob locator   
  18.   INSERT INTO waterfalls  
  19.     (falls_name, falls_directions)  
  20.   VALUES  
  21.     ('Munising Falls', empty_clob());  
  22.   --獲得lob locator,上面插入的數據,因爲我們插入的是一個empty_clob(),那麼lob locator不指向任何數據,雖然給變量分配了只   
  23.   SELECT falls_directions  
  24.     INTO directions  
  25.     FROM waterfalls  
  26.    WHERE falls_name = 'Munising Falls';  
  27.   IF directions IS NULL THEN  
  28.     dbms_output.put_line('directions is NULL');  
  29.   ELSE  
  30.     dbms_output.put_line('directions is not NULL'); --打印此句   
  31.   END IF;  
  32.   dbms_output.put_line('Length = ' || dbms_lob.getlength(directions)); --結果爲o   
  33. END;  
注意: 
  1. 上面例子中的empty_clob()是oracle的內置函數,創建了一個lob locator。但是我們沒有讓它指向任何數據,所以是empty。而且通過select語句給變量directions分配了lob locator,所以不是null,但是length爲0,故爲empty。 
  2. 在基本類型中,我們判斷一個變量是不是有數據,只要is null就可以了。但是在lob類型中我們從以上的例子看出來是不正確的。Lob首先必須判斷is null看是否分配lob locator,如果分配了還需要進一步檢查length是否爲0,看是否是empty,所以完整的是下面這樣:
  1. IF some_clob IS NULL THEN  
  2.     --如果is null爲true表示未分配,肯定沒有數據   
  3.   ELSIF dbms_lob.getlength(some_clob) = 0 THEN  
  4.     --分配了length爲0,也沒有數據   
  5.   ELSE  
  6.     --有數據   
  7.   END IF;  
  1. IF some_clob IS NULL THEN  
  2.     --如果is null爲true表示未分配,肯定沒有數據   
  3.   ELSIF dbms_lob.getlength(some_clob) = 0 THEN  
  4.     --分配了length爲0,也沒有數據   
  5.   ELSE  
  6.     --有數據   
  7.   END IF;  

3.建立LOB

在上面我們使用empty_clob()建立了一個空的clob,lob
locator只是一個指針,真正的數據是存儲在磁盤中或數據庫文件中。我 們先建立一個空的clob,然後我們可以update來讓變量真正指向有數據的lob。Empty_clob()可以用來處理clob和nclob。在oracle 8i中可以使用temporary lob達到同樣的效果。



4. 
向LOB裏寫入數據
當獲得一個有效的lob
locator之後,就可以使用dbms_lob包的下列procedure向lob中寫入數據。 


       DBMS_LOB.WRITE:允許自動寫入數據到lob中。 

       DBMS_LOB.WRITEAPPEND:向lob的末尾寫入數據。
  1. --write lob   
  2. DECLARE  
  3.   directions      CLOB;  
  4.   amount          BINARY_INTEGER;  
  5.   offset          INTEGER;  
  6.   first_direction VARCHAR2(100);  
  7.   more_directions VARCHAR2(500);  
  8. BEGIN  
  9.   --Delete any existing rows for 'Munising Falls' so that this   
  10.   --example can be executed multiple times   
  11.   DELETE FROM waterfalls WHERE falls_name = 'Munising Falls';  
  12.   --Insert a new row using EMPTY_CLOB(  ) to create a LOB locator   
  13.   INSERT INTO waterfalls  
  14.     (falls_name, falls_directions)  
  15.   VALUES  
  16.     ('Munising Falls', empty_clob());  
  17.   --Retrieve the LOB locator created by the previous INSERT statement   
  18.   SELECT falls_directions  
  19.     INTO directions  
  20.     FROM waterfalls  
  21.    WHERE falls_name = 'Munising Falls';  
  22.   --Open the LOB; not strictly necessary, but best to open/close LOBs.   
  23.   dbms_lob.open(directions, dbms_lob.lob_readwrite);  
  24.   --Use DBMS_LOB.WRITE to begin   
  25.   first_direction := 'Follow I-75 across the Mackinac Bridge.';  
  26.   amount          := length(first_direction); --number of characters to write   
  27.   offset          := 1; --begin writing to the first character of the CLOB   
  28.   dbms_lob.write(directions, amount, offset, first_direction);  
  29.   --Add some more directions using DBMS_LOB.WRITEAPPEND   
  30.   more_directions := ' Take US-2 west from St. Ignace to Blaney Park.' ||  
  31.                      ' Turn north on M-77 and drive to Seney.' ||  
  32.                      ' From Seney, take M-28 west to Munising.';  
  33.   dbms_lob.writeappend(directions,  
  34.                        length(more_directions),  
  35.                        more_directions);  
  36.   --Add yet more directions   
  37.   more_directions := ' In front of the paper mill, turn right on H-58.' ||  
  38.                      ' Follow H-58 to Washington Street. Veer left onto' ||  
  39.                      ' Washington Street. You''ll find the Munising' ||  
  40.                      ' Falls visitor center across from the hospital at' ||  
  41.                      ' the point where Washington Street becomes' ||  
  42.                      ' Sand Point Road.';  
  43.   dbms_lob.writeappend(directions,  
  44.                        length(more_directions),  
  45.                        more_directions);  
  46.   --Close the LOB, and we are done.   
  47.   dbms_lob.close(directions);  
  48. END;  
  1. --write lob   
  2. DECLARE  
  3.   directions      CLOB;  
  4.   amount          BINARY_INTEGER;  
  5.   offset          INTEGER;  
  6.   first_direction VARCHAR2(100);  
  7.   more_directions VARCHAR2(500);  
  8. BEGIN  
  9.   --Delete any existing rows for 'Munising Falls' so that this   
  10.   --example can be executed multiple times   
  11.   DELETE FROM waterfalls WHERE falls_name = 'Munising Falls';  
  12.   --Insert a new row using EMPTY_CLOB(  ) to create a LOB locator   
  13.   INSERT INTO waterfalls  
  14.     (falls_name, falls_directions)  
  15.   VALUES  
  16.     ('Munising Falls', empty_clob());  
  17.   --Retrieve the LOB locator created by the previous INSERT statement   
  18.   SELECT falls_directions  
  19.     INTO directions  
  20.     FROM waterfalls  
  21.    WHERE falls_name = 'Munising Falls';  
  22.   --Open the LOB; not strictly necessary, but best to open/close LOBs.   
  23.   dbms_lob.open(directions, dbms_lob.lob_readwrite);  
  24.   --Use DBMS_LOB.WRITE to begin   
  25.   first_direction := 'Follow I-75 across the Mackinac Bridge.';  
  26.   amount          := length(first_direction); --number of characters to write   
  27.   offset          := 1; --begin writing to the first character of the CLOB   
  28.   dbms_lob.write(directions, amount, offset, first_direction);  
  29.   --Add some more directions using DBMS_LOB.WRITEAPPEND   
  30.   more_directions := ' Take US-2 west from St. Ignace to Blaney Park.' ||  
  31.                      ' Turn north on M-77 and drive to Seney.' ||  
  32.                      ' From Seney, take M-28 west to Munising.';  
  33.   dbms_lob.writeappend(directions,  
  34.                        length(more_directions),  
  35.                        more_directions);  
  36.   --Add yet more directions   
  37.   more_directions := ' In front of the paper mill, turn right on H-58.' ||  
  38.                      ' Follow H-58 to Washington Street. Veer left onto' ||  
  39.                      ' Washington Street. You''ll find the Munising' ||  
  40.                      ' Falls visitor center across from the hospital at' ||  
  41.                      ' the point where Washington Street becomes' ||  
  42.                      ' Sand Point Road.';  
  43.   dbms_lob.writeappend(directions,  
  44.                        length(more_directions),  
  45.                        more_directions);  
  46.   --Close the LOB, and we are done.   
  47.   dbms_lob.close(directions);  
  48. END;  
在這個例子裏,我們使用了write 和writeappend這兩個過程來插入數據到lob中。因爲開始的時候,我們插入了一個空的lob locator。要注意一點,我們最後使用了dbms_lob.close方法關閉lob。這是一個好的方法,特別是在處理oracle text的時候,任何oracle text domain和function-based indexes被update是在wirte和writeappend的時候調用的,而不是在close的時候被update的。        
我們向lob中寫入數據的時候,沒有必要更新表中的列。因爲它保存的只是一個locator,我們的變量也獲得同樣的locator,當我們寫入數據去lob的時候,locator並沒有改變。改變的只是locator指向的物理數據。 


5.
從lob中讀取數據

步驟:a.通過select查詢獲得lob locator初始化lob變量。2.調用dbms_lob.read過程讀取lob數據。 
下面是dbms_lob.read過程的定義,注意參數.  
 
  1. PROCEDURE READ(lob_loc IN BLOB, --初始化後的lob變量lob locator   
  2.                amount  IN OUT NOCOPY INTEGER--讀取的數量(clob爲字符數,blob,bfile是字節數)   
  3.                offset  IN INTEGER--開始讀取位置   
  4.                buffer  OUT RAW); --讀到的數據,raw要顯示用轉換函數,見bfile   
  5. PROCEDURE READ(lob_loc IN CLOB CHARACTER SET any_cs,  
  6.                amount  IN OUT NOCOPY INTEGER,  
  7.                offset  IN INTEGER,  
  8.                buffer  OUT VARCHAR2 CHARACTER SET lob_loc%charset);  
  9. PROCEDURE READ(file_loc IN BFILE,  
  10.                amount   IN OUT NOCOPY INTEGER,  
  11.                offset   IN INTEGER,  
  12.                buffer   OUT RAW);  
  1. PROCEDURE READ(lob_loc IN BLOB, --初始化後的lob變量lob locator   
  2.                amount  IN OUT NOCOPY INTEGER--讀取的數量(clob爲字符數,blob,bfile是字節數)   
  3.                offset  IN INTEGER--開始讀取位置   
  4.                buffer  OUT RAW); --讀到的數據,raw要顯示用轉換函數,見bfile   
  5. PROCEDURE READ(lob_loc IN CLOB CHARACTER SET any_cs,  
  6.                amount  IN OUT NOCOPY INTEGER,  
  7.                offset  IN INTEGER,  
  8.                buffer  OUT VARCHAR2 CHARACTER SET lob_loc%charset);  
  9. PROCEDURE READ(file_loc IN BFILE,  
  10.                amount   IN OUT NOCOPY INTEGER,  
  11.                offset   IN INTEGER,  
  12.                buffer   OUT RAW);  

  1. --從lob中讀取數據   
  2. DECLARE  
  3.   directions   CLOB;  
  4.   directions_1 VARCHAR2(300);  
  5.   directions_2 VARCHAR2(300);  
  6.   chars_read_1 BINARY_INTEGER;  
  7.   chars_read_2 BINARY_INTEGER;  
  8.   offset       INTEGER;  
  9. BEGIN  
  10.   --首先獲得一個lob locator   
  11.   SELECT falls_directions  
  12.     INTO directions  
  13.     FROM waterfalls  
  14.    WHERE falls_name = 'Munising Falls';  
  15.   --記錄開始讀取位置   
  16.   offset := 1;  
  17.   --嘗試讀取229個字符,chars_read_1將被實際讀取的字符數更新   
  18.   chars_read_1 := 229;  
  19.   dbms_lob.read(directions, chars_read_1, offset, directions_1);  
  20.   --當讀取229個字符之後,更新offset,再讀取225個字符   
  21.   IF chars_read_1 = 229 THEN  
  22.     offset       := offset + chars_read_1; --offset變爲offset+chars_read_1,也就是從300開始   
  23.     chars_read_2 := 255;  
  24.     dbms_lob.read(directions, chars_read_2, offset, directions_2);  
  25.   ELSE  
  26.     chars_read_2 := 0; --否則後面不在讀取   
  27.     directions_2 := '';  
  28.   END IF;  
  29.   --顯示讀取的字符數   
  30.   dbms_output.put_line('Characters read = ' ||  
  31.                        to_char(chars_read_1 + chars_read_2));  
  32.   --顯示結果   
  33.   dbms_output.put_line(directions_1);  
  34.   dbms_output.put_line(length(directions_1));  
  35.   dbms_output.put_line(directions_2);  
  36.   dbms_output.put_line(length(directions_2));  
  37. END;  
  1. --從lob中讀取數據   
  2. DECLARE  
  3.   directions   CLOB;  
  4.   directions_1 VARCHAR2(300);  
  5.   directions_2 VARCHAR2(300);  
  6.   chars_read_1 BINARY_INTEGER;  
  7.   chars_read_2 BINARY_INTEGER;  
  8.   offset       INTEGER;  
  9. BEGIN  
  10.   --首先獲得一個lob locator   
  11.   SELECT falls_directions  
  12.     INTO directions  
  13.     FROM waterfalls  
  14.    WHERE falls_name = 'Munising Falls';  
  15.   --記錄開始讀取位置   
  16.   offset := 1;  
  17.   --嘗試讀取229個字符,chars_read_1將被實際讀取的字符數更新   
  18.   chars_read_1 := 229;  
  19.   dbms_lob.read(directions, chars_read_1, offset, directions_1);  
  20.   --當讀取229個字符之後,更新offset,再讀取225個字符   
  21.   IF chars_read_1 = 229 THEN  
  22.     offset       := offset + chars_read_1; --offset變爲offset+chars_read_1,也就是從300開始   
  23.     chars_read_2 := 255;  
  24.     dbms_lob.read(directions, chars_read_2, offset, directions_2);  
  25.   ELSE  
  26.     chars_read_2 := 0; --否則後面不在讀取   
  27.     directions_2 := '';  
  28.   END IF;  
  29.   --顯示讀取的字符數   
  30.   dbms_output.put_line('Characters read = ' ||  
  31.                        to_char(chars_read_1 + chars_read_2));  
  32.   --顯示結果   
  33.   dbms_output.put_line(directions_1);  
  34.   dbms_output.put_line(length(directions_1));  
  35.   dbms_output.put_line(directions_2);  
  36.   dbms_output.put_line(length(directions_2));  
  37. END;  

Dbms_lob.read的第2個參數是傳遞要讀取的數量。對於clob是字符數,blob和bfile都是字節數。它是隨着讀取的數目自動更新的,offset不會更新。所以分佈讀取需要手動更新offset,下個offset是上一個offset+讀取的數量。我們可以通過dbms_lob.get_length(lob_locator)獲得這個lob的長度,結果clob是字符數,blob和bfile是字節數,然後分佈讀取。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章