傳統 ADO 透過 OLE DB/ODBC 訪問 Oracle CLOB/BLOB(>4000 字元)時,常遇到「資料截斷」、「型別不匹配」、「資料流不支援」等問題。本文以 VB6/ASP 為例,示範存儲過程 + PL/SQL 表類型 + ADO Command模式,優雅解決大字段讀寫。
PROCEDURE,使用 TABLE OF VARCHAR2 INDEX BY BINARY_INTEGER 模擬陣列輸出大量文本。CommandType = adCmdText,呼叫 {call pkg.proc(?, {resultset N, cols...})} 語法綁定參數與結果集。CursorLocation = adUseClient 確保支援斷開式記錄集。-- 1. 建表
DROP TABLE person;
CREATE TABLE person (
ssn NUMBER(9) PRIMARY KEY,
fname VARCHAR2(15),
lname VARCHAR2(20),
bio CLOB -- 大文本欄位
);
INSERT INTO person VALUES (555662222, 'Sam', 'Goodwin', '長文本內容...');
INSERT INTO person VALUES (555882222, 'Kent', 'Clark', '另一段長文本...');
COMMIT;
-- 2. Package Spec
CREATE OR REPLACE PACKAGE pkg_person AS
TYPE t_ssn IS TABLE OF NUMBER(10) INDEX BY BINARY_INTEGER;
TYPE t_fname IS TABLE OF VARCHAR2(15) INDEX BY BINARY_INTEGER;
TYPE t_lname IS TABLE OF VARCHAR2(20) INDEX BY BINARY_INTEGER;
TYPE t_bio IS TABLE OF CLOB INDEX BY BINARY_INTEGER; -- CLOB 陣列
PROCEDURE get_all (ssn OUT t_ssn, fname OUT t_fname, lname OUT t_lname, bio OUT t_bio);
PROCEDURE get_one (p_ssn IN NUMBER, ssn OUT t_ssn, fname OUT t_fname, lname OUT t_lname, bio OUT t_bio);
END pkg_person;
/
-- 3. Package Body
CREATE OR REPLACE PACKAGE BODY pkg_person AS
PROCEDURE get_all (ssn OUT t_ssn, fname OUT t_fname, lname OUT t_lname, bio OUT t_bio) IS
CURSOR c IS SELECT ssn, fname, lname, bio FROM person;
idx NUMBER := 1;
BEGIN
FOR r IN c LOOP
ssn(idx) := r.ssn;
fname(idx) := r.fname;
lname(idx) := r.lname;
bio(idx) := r.bio; -- 直接賦值 CLOB
idx := idx + 1;
END LOOP;
END;
PROCEDURE get_one (p_ssn IN NUMBER, ssn OUT t_ssn, fname OUT t_fname, lname OUT t_lname, bio OUT t_bio) IS
CURSOR c IS SELECT ssn, fname, lname, bio FROM person WHERE ssn = p_ssn;
idx NUMBER := 1;
BEGIN
FOR r IN c LOOP
ssn(idx) := r.ssn;
fname(idx) := r.fname;
lname(idx) := r.lname;
bio(idx) := r.bio;
idx := idx + 1;
END LOOP;
END;
END pkg_person;
/
<%@ Language=VBScript %>
<%
Option Explicit
Dim Cn, Cmd, Rs, ConnStr, SQL
' 連接字串 - 使用 Microsoft ODBC for Oracle 或 Oracle Provider for OLE DB
ConnStr = "Provider=MSDAORA.1;User ID=scott;Password=tiger;Data Source=ORCL;Persist Security Info=False"
' 或: "Driver={Microsoft ODBC for Oracle};Server=ORCL;UID=scott;PWD=tiger;"
Set Cn = Server.CreateObject("ADODB.Connection")
Cn.CursorLocation = 3 ' adUseClient
Cn.Open ConnStr
' 取得所有記錄(含 CLOB)
SQL = "{call pkg_person.get_all({resultset 4, ssn, fname, lname, bio})}"
Set Cmd = Server.CreateObject("ADODB.Command")
With Cmd
Set .ActiveConnection = Cn
.CommandText = SQL
.CommandType = 1 ' adCmdText
End With
Set Rs = Server.CreateObject("ADODB.Recordset")
Rs.CursorType = 3 ' adOpenStatic
Rs.LockType = 1 ' adLockReadOnly
Set Rs.Source = Cmd
Rs.Open
' 讀取 CLOB 欄位(Rs("bio") 直接回傳字串,ADO 自動處理 LOB 讀取)
Do Until Rs.EOF
Response.Write "SSN: " & Rs("ssn") & "<br>"
Response.Write "Name: " & Rs("fname") & " " & Rs("lname") & "<br>"
Response.Write "Bio Length: " & Len(Rs("bio")) & "<br><hr>"
Rs.MoveNext
Loop
Rs.Close
Cn.Close
Set Rs = Nothing: Set Cmd = Nothing: Set Cn = Nothing
%>
| 問題 | 解決方案 |
|---|---|
| CLOB 超過 4000 字元無法直接 SELECT | PL/SQL 陣列輸出 + {resultset N, ...} 語法,ADO 將 LOB 當作 LONG VARCHAR 流式讀取 |
| ADO 無法綁定 PL/SQL TABLE 類型 | 使用 Oracle 專屬 {call ... {resultset ...}} 語法,由 OLE DB Provider 解析為多行結果集 |
| 游標在關閉連接後失效 | CursorLocation = adUseClient 建立客戶端游標,數據緩存在本地 |
| 寫入 CLOB | 改用 DBMS_LOB.WRITEAPPEND 或 EMPTY_CLOB() + RETURNING INTO 綁定 adLongVarChar 參數分塊寫入 |
SQL = "DECLARE l_clob CLOB; BEGIN " & _
" INSERT INTO person(ssn, fname, lname, bio) VALUES (?, ?, ?, EMPTY_CLOB()) " & _
" RETURNING bio INTO l_clob; " & _
" DBMS_LOB.WRITEAPPEND(l_clob, ?, ?); " & _
" COMMIT; END;"
Set Cmd = Server.CreateObject("ADODB.Command")
Cmd.ActiveConnection = Cn
Cmd.CommandText = SQL
Cmd.CommandType = 1
' 參數順序:ssn, fname, lname, length, data
Cmd.Parameters.Append Cmd.CreateParameter("p_ssn", 3, 1, , 999999999) ' adInteger
Cmd.Parameters.Append Cmd.CreateParameter("p_fname", 200, 1, 20, "New") ' adVarChar
Cmd.Parameters.Append Cmd.CreateParameter("p_lname", 200, 1, 20, "User") ' adVarChar
Cmd.Parameters.Append Cmd.CreateParameter("p_len", 3, 1, , Len(longText)) ' adInteger
Cmd.Parameters.Append Cmd.CreateParameter("p_data", 201, 1, Len(longText), longText) ' adLongVarChar
Cmd.Execute
MSDAORA 或 OraOLEDB.Oracle,舊版 MSDASQL+ODBC 不支援 LOB 參數綁定。CursorLocation 必須設 adUseClient (3)。ADO Connection String 是否含 ChunkSize 參數,或改用 GetChunk/AppendChunk 分塊讀寫。AMERICAN_AMERICA.ZHS16GBK 或 AL32UTF8),資料庫、Client、網頁編碼三統一。新專案建議改用 Oracle Managed Data Access (ODP.NET Managed Driver) + ASP.NET Core / Entity Framework Core,原生支援 CLOB/BLOB、JSON、XMLType,無需 PL/SQL 陣列繞道,效能與維護性大幅提升。