// 錯誤:ODBC
"Driver={Microsoft ODBC for Oracle};Server=ORCL;Uid=scott;Pwd=tiger;"
// 正確:MSDAORA
"Provider=MSDAORA.1;Password=tiger;User ID=scott;Data Source=ORCL;Persist Security Info=True"
CREATE OR REPLACE PACKAGE pkg_emp AS
TYPE t_cursor IS REF CURSOR;
PROCEDURE get_emp(p_deptno NUMBER, p_rc OUT t_cursor);
END pkg_emp;
/
CREATE OR REPLACE PACKAGE BODY pkg_emp AS
PROCEDURE get_emp(p_deptno NUMBER, p_rc OUT t_cursor) IS
BEGIN
IF p_deptno = 0 THEN
OPEN p_rc FOR SELECT empno, ename, job, sal FROM emp;
ELSE
OPEN p_rc FOR SELECT empno, ename, job, sal FROM emp WHERE deptno = p_deptno;
END IF;
END get_emp;
END pkg_emp;
/
或無 Package 直接:PROCEDURE get_emp(p_deptno NUMBER, p_rc OUT SYS_REFCURSOR)。
_ConnectionPtr pConn(__uuidof(Connection));
pConn->Open(_bstr_t(connStr), L"", L"", adConnectUnspecified);
_RecordsetPtr pRs(__uuidof(Recordset));
// 關鍵:ODBC CALL 語法,輸出參數省略
pRs->Open(_bstr_t("{call pkg_emp.get_emp(10)}"),
_variant_t((IDispatch*)pConn),
adOpenStatic, adLockReadOnly, adCmdText);
// 處理多結果集 (Oracle 只回一個 Ref Cursor,但通用寫法)
long recordsAffected = 0;
while (pRs) {
while (!pRs->EndOfFile) {
// 讀取欄位...
pRs->MoveNext();
}
pRs = pRs->NextRecordset(&recordsAffected);
}
pRs->Close();
pConn->Close();
採用 {call proc_name(param1, param2, ...)} 格式:
{call get_emp(10)} (輸出參數同樣可省略)。{call pkg_emp.get_emp(10)}。VC++ 代碼**零修改**支援雙資料庫,僅換連接字串與 SQL 字串常數。
// SQL Server 風格 (Oracle 同理)
DECLARE @ret INT, @msg NVARCHAR(200);
EXEC check_login 'admin', 'pwd', @ret OUTPUT, @msg OUTPUT;
SELECT @ret AS RetCode, @msg AS Msg;
ADO 執行上述批次 SQL,結果集包含 RetCode、Msg 兩欄,統一用 pRs->Fields->GetItem() 讀取。
adCmdText 而非 adCmdStoredProc,避免 ADO 內部參數探查開銷。CursorLocation = adUseClient 客戶端游標,支援斷開連線後操作 Recordset。pConn->ConnectionString 含 OLE DB Services=-1 (預設啟用池化)。_com_error 捕獲 HRESULT,記錄 ErrorInfo->GetDescription()。此模式已在多個生產系統穩定運行 10+ 年,核心優勢在於 SQL 字串即介面契約,VC++ 層完全解耦預存程序簽名變更。