執行 SQL 查詢後,如何高效、正確地處理回傳的結果集,是 ODBC 程式設計的關鍵。本文深入解析 SQLNumResultCols、SQLBindCol、SQLFetch、SQLCloseCursor 的運作機制與實戰技巧,並以經典的「經緯度」比喻幫助理解游標與欄位綁定的關係。
SQLNumResultColsSQLBindColSQLFetchSQLCloseCursorSQLNumResultCols proto StatementHandle:DWORD, pNumCols:DWORD
; 回傳:pNumCols 指向的 DWORD = 欄位數
; 若為 0 → 無結果集(如 INSERT/UPDATE/DDL)
適用場景:使用者動態輸入 SQL 陳述式,無法預知是否會傳回資料列。
SQLBindCol proto StatementHandle:DWORD,
ColumnNumber:DWORD, ; 1-based,0 為書籤欄
TargetType:DWORD, ; SQL_C_CHAR, SQL_C_LONG, SQL_C_DOUBLE 等
TargetValuePtr:DWORD, ; 緩衝區位址
BufferLength:DWORD, ; 緩衝區長度(位元組)
pStrLenOrIndPtr:DWORD ; 實際資料長度/指標變數位址
範例:綁定第 1 欄字串到 21 位元組緩衝區
.data?
buffer db 21 dup(?)
DataLength dd ?
.code
invoke SQLBindCol, hStmt, 1, SQL_C_CHAR, addr buffer, 21, addr DataLength
呼叫 SQLFetch 後,buffer 含資料,DataLength 含實際長度(不含結束符)。
SQLFetch proto StatementHandle:DWORD
; 回傳:SQL_SUCCESS / SQL_SUCCESS_WITH_INFO / SQL_NO_DATA(=100)
游標機制:
SQLFetch 將游標前移一列,並將綁定欄位資料填入緩衝區。SQL_NO_DATA,應跳出迴圈。invoke SQLCloseCursor, hStmt
invoke SQLFreeHandle, SQL_HANDLE_STMT, hStmt
務必在處理完結果集後呼叫,釋放鎖定與控制代碼。
.data?
buffer db 21 dup(?)
DataLength dd ?
row dd 0
.code
; 綁定欄位
invoke SQLBindCol, hStmt, 1, SQL_C_CHAR, addr buffer, 21, addr DataLength
; 可重複綁定多欄:第 2、3 欄...
mov row, 0
.fetch_loop:
; 選擇性清空緩衝區(或改用 DataLength 判斷)
mov byte ptr [buffer], 0
invoke SQLFetch, hStmt
.if ax == SQL_SUCCESS || ax == SQL_SUCCESS_WITH_INFO
; 處理 buffer 中的資料
; 例如送入 ListView、列印、寫檔...
inc row
jmp .fetch_loop
.endif
; SQL_NO_DATA 或錯誤 → 結束
invoke SQLCloseCursor, hStmt
| 主題 | 說明 |
|---|---|
| 書籤欄 | ColumnNumber=0 為書籤,可用於隨機存取,需驅動程式支援。 |
| 資料長度變數 | 優先檢查 pStrLenOrIndPtr 指向的值,而非假設緩衝區以 0 結尾(二進位資料可能含 0)。 |
| 緩衝區大小 | 預留足夠空間(含結束符),過小會導致截斷與 SQL_SUCCESS_WITH_INFO + SQLSTATE 01004。 |
| 多結果集 | 若預存程序傳回多結果集,需呼叫 SQLMoreResults 切換。 |
| 游標類型 | 預設為前進唯讀;需捲動游標時,分配 Stmt 前設定 SQL_ATTR_CURSOR_TYPE。 |
「還記得地球的經緯嗎?我們通過
SQLBindCol來定位經度(欄),用游標來定義緯度(列),用SQLFetch來獲得座標,而結果集就是地球。」(譯者原文)
這個比喻精準捕捉了「欄位綁定=固定經度」、「游標移動=改變緯度」的正交關係。
invoke SQLFetch, hStmt
.if ax == SQL_ERROR
; 呼叫 SQLError 取得詳細錯誤資訊
invoke SQLError, hEnv, hConn, hStmt, addr SqlState, addr NativeError, addr ErrMsg, sizeof ErrMsg, addr TextLen
; 顯示或記錄錯誤
.elseif ax == SQL_NO_DATA
; 正常結束
.else
; 成功取得一列
.endif
掌握 SQLBindCol + SQLFetch 迴圈模式,是 ODBC 程式設計的核心技能。關鍵在於:正確綁定型別與緩衡區、善用長度指標變數、嚴格檢查回傳碼、完整釋放資源。本文提供的範本可直接套用於實務專案,並能延伸支援捲動游標、多結果集、非同步執行等進階功能。