首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

ODBC API 直接操作資料庫完整指南:配置、連線、DDL/DML 與動態建庫

     編輯:關於VC++

為何選擇 ODBC API

相較 ADO/DAO 的高層封裝,ODBC API 雖代碼較繁,但具備:

  • **零依賴**:僅需 odbc32.lib、odbccp32.lib,無需 MDAC/ACE 提供者。
  • **高效能**:直接對話驅動程式,無 COM 封送開銷。
  • **可移植**:標準 ISO/ODBC 介面,易移植至 Linux/unixODBC。
  • **透明度**:清楚掌握環境/連線/陳述式三層控制碼生命週期,利於除錯與資源控制。

開發環境準備


#include 
#include 
#include   // SQLConfigDataSource
#pragma comment(lib, "odbc32.lib")
#pragma comment(lib, "odbccp32.lib")

1. 系統 DSN 自動配置


BOOL ConfigDSN() {
    SQLRETURN ret = SQLConfigDataSource(NULL, ODBC_ADD_SYS_DSN,
        _T("SQL Server"),
        _T("DSN=MyDSN\0Server=(local)\0Database=master\0\0"));
    return (ret == TRUE);
}
  • 字串以雙 \0 結尾,屬性間單 \0 分隔。
  • 亦可用 ODBC_REMOVE_SYS_DSN 移除、ODBC_CONFIG_SYS_DSN 修改。

2. 標準連線流程(三層控制碼)


SQLHENV hEnv = SQL_NULL_HANDLE;
SQLHDBC hDbc = SQL_NULL_HANDLE;

auto check = [](SQLRETURN rc, SQLSMALLINT handleType, SQLHANDLE handle, const char* msg) {
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
        SQLWCHAR sqlState[6], msgBuf[1024];
        SQLINTEGER native;
        SQLSMALLINT len;
        SQLGetDiagRec(handleType, handle, 1, sqlState, &native, msgBuf, 1024, &len);
        // 記錄錯誤...
        return false;
    }
    return true;
};

if (!check(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv), ...)) return FALSE;
if (!check(SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0), ...)) return FALSE;
if (!check(SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc), ...)) return FALSE;

SQLRETURN ret = SQLConnect(hDbc,
    (SQLTCHAR*)dsn, SQL_NTS,
    (SQLTCHAR*)uid, SQL_NTS,
    (SQLTCHAR*)pwd, SQL_NTS);
if (!check(ret, SQL_HANDLE_DBC, hDbc, "Connect")) {
    SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
    SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
    return FALSE;
}
// 連線成功,保存 hEnv/hDbc 供後續使用

3. DDL:建立/刪除/修改資料表


BOOL ExecuteDDL(SQLHDBC hDbc, const CString& sql) {
    SQLHSTMT hStmt;
    if (!check(SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt), ...)) return FALSE;
    SQLRETURN ret = SQLExecDirect(hStmt, (SQLTCHAR*)(LPCTSTR)sql, SQL_NTS);
    BOOL ok = check(ret, SQL_HANDLE_STMT, hStmt, "ExecDirect");
    SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
    return ok;
}

// 範例
ExecuteDDL(hDbc, _T("CREATE TABLE Users (Id INT PRIMARY KEY, Name NVARCHAR(50))"));
ExecuteDDL(hDbc, _T("DROP TABLE OldTable"));
ExecuteDDL(hDbc, _T("ALTER TABLE Users ADD Email NVARCHAR(100)"));

4. DML:參數綁定與資料操作

插入(參數化,防注入)


BOOL InsertUser(SQLHDBC hDbc, int id, const CString& name) {
    SQLHSTMT hStmt;
    SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);
    SQLPrepare(hStmt, (SQLTCHAR*)_T("INSERT INTO Users (Id, Name) VALUES (?, ?)"), SQL_NTS);

    SQLINTEGER cbId = 0, cbName = SQL_NTS;
    SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &id, 0, &cbId);
    SQLBindParameter(hStmt, 2, SQL_PARAM_INPUT, SQL_C_TCHAR, SQL_WVARCHAR, 50, 0,
        (SQLPOINTER)(LPCTSTR)name, 0, &cbName);

    SQLRETURN ret = SQLExecute(hStmt);
    BOOL ok = check(ret, SQL_HANDLE_STMT, hStmt, "Execute");
    SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
    return ok;
}

查詢(綁定資料行 → 擷取逐列)


BOOL QueryUsers(SQLHDBC hDbc, std::vector>& out) {
    SQLHSTMT hStmt;
    SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);
    SQLExecDirect(hStmt, (SQLTCHAR*)_T("SELECT Id, Name FROM Users"), SQL_NTS);

    int id; CString name;
    SQLLEN cbId, cbName;
    SQLBindCol(hStmt, 1, SQL_C_LONG, &id, sizeof(id), &cbId);
    SQLBindCol(hStmt, 2, SQL_C_TCHAR, name.GetBuffer(100), 100*sizeof(TCHAR), &cbName);

    while (SQLFetch(hStmt) == SQL_SUCCESS) {
        name.ReleaseBuffer();
        out.emplace_back(id, name);
        name.GetBuffer(100); // 重置緩衝
    }
    name.ReleaseBuffer();
    SQLFreeHandle(SQL_HANDLE_STMT, hStmt);
    return true;
}

更新/刪除

同插入,改用 UPDATE ... SET ... WHERE ? / DELETE ... WHERE ? 並綁定參數執行 SQLExecute。

5. 進階:動態建立資料庫(以 SQL Server master 為例)

  1. 連線 master 資料庫。
  2. 查詢 sysdatabases 確認目標資料庫是否存在:SELECT name FROM sysdatabases WHERE name='MyDB'。
  3. 不存在則執行 CREATE DATABASE MyDB。
  4. 切換連線至 MyDB(SQLSetConnectAttr 或重新 SQLConnect)。
  5. 查詢 sysobjects 確認資料表是否存在,缺則建立。

此模式適用於安裝程式、首次啟動自動初始化 Schema 等場景。

6. 資源釋放順序(逆向分配)


SQLFreeHandle(SQL_HANDLE_STMT, hStmt); // 每個陳述式用完即釋
SQLDisconnect(hDbc);
SQLFreeHandle(SQL_HANDLE_DBC, hDbc);
SQLFreeHandle(SQL_HANDLE_ENV, hEnv);

常見錯誤與最佳實踐

總結

ODBC API 雖屬底層,但掌握「環境→連線→陳述式」三層控制碼與「準備→綁定→執行→擷取」四步驟,即可實現高效、可移植、無外部依賴的資料庫操作。本文提供的完整代碼範例可直接作為專案資料存取層的基礎骨架。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
問題解決
中文亂碼連線字串加 CharacterSet=UTF-8,或使用 Unicode 版 API (SQLWCHAR/SQLExecDirectW)
連線逾時SQLSetConnectAttr(hDbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)30, 0)
游標類型不支援SQLSetStmtAttr(hStmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, 0)
參數綁定類型不匹配嚴格對應 SQL_C_* (C 類型) ↔ SQL_* (SQL 類型),參考 MSDN 附錄 D
記憶體洩漏每個 AllocHandle 必配 FreeHandle,建議封裝 RAII 類別