在這個快速入門中,你會在 Windows、Linux 或 macOS 上建立一個 C++ 控制台應用程式。 應用程式透過使用 Microsoft ODBC Driver 18 for SQL Server 連接資料庫AdventureWorksLT,綁定查詢參數,執行查詢,並讀取結果列。
Prerequisites
- Microsoft ODBC Driver 18 for SQL Server. 安裝 Windows、Linux 或 macOS 的驅動程式。
- C++17 編譯器以及該平台的 ODBC 開發檔案:
- 在 Windows 上,請安裝 Visual Studio 2022 或 Visual Studio 2022 Build Tools,並選取 Desktop development with C++ 工作負載。 Windows SDK 提供 ODBC 標頭及
odbc32.lib。 - 在 Linux 上,安裝 C++ 編譯器和 unixODBC 開發套件作為你的發行版。 封裝提供 ODBC 標頭及
libodbc。 - 在 macOS 上,安裝 Xcode 命令列工具和 Homebrew 的 unixODBC。
- 在 Windows 上,請安裝 Visual Studio 2022 或 Visual Studio 2022 Build Tools,並選取 Desktop development with C++ 工作負載。 Windows SDK 提供 ODBC 標頭及
- Azure SQL Database 或 SQL Server 中的一個資料庫,包含
AdventureWorksLT範例資料。 對於 Azure SQL Database,建立單一資料庫時,選擇範例資料來源。 對於 SQL Server,請從 AdventureWorks 範例資料庫還原AdventureWorksLT備份。
確認驅動程式
確認驅動管理員能找到 Microsoft ODBC Driver 18 for SQL Server。
每個指令都應該列出 ODBC Driver 18 for SQL Server。 如果驅動程式沒有列出,請在繼續前重新安裝。
設定連線
應用程式會從 ODBC_CONNECTION_STRING 環境變數讀取完整的連線字串。 它不會顯示 連接字串,也不會接受它作為命令列參數。
使用適用於你的資料庫和驗證方法的 Driver 18 連線字串。 以下 SQL 認證範例在 Windows、Linux 和 macOS 上運作,前提是資料庫允許 SQL 認證:
Driver={ODBC Driver 18 for SQL Server};Server=tcp:<server>,1433;Database=<database>;UID=<user_id>;PWD=<password>;Encrypt=yes;TrustServerCertificate=no;
關於 Microsoft Entra 認證選項,請參見「使用 Microsoft Entra ID 搭配 ODBC 驅動程式」。 關於所有支援的設定,請參閱 DSN 與 連接字串 關鍵字與屬性。
Microsoft ODBC 驅動程式 18 預設啟用加密功能。 指定 Encrypt=yes 讓應用程式的需求明確。 保持 TrustServerCertificate=no 在生產環境,讓驅動程式驗證伺服器憑證。 憑證必須將伺服器名稱與鏈條與客戶信任的認證機構相匹配。 有關設定指引,請參見憑證驗證失敗。
注意事項
TrustServerCertificate=yes 跳過憑證驗證。 在設定客戶信任的憑證時,只用它來做孤立的本地開發。 不要在生產環境中使用它。
設定環境變數時,不要把 連接字串 加入你的 shell 歷史。
VS 2022 的 Open Developer PowerShell。 執行這些命令,然後在提示字元處貼上連線字串:
$secureConnectionString = Read-Host "ODBC connection string" -AsSecureString
$env:ODBC_CONNECTION_STRING = [System.Net.NetworkCredential]::new(
"", $secureConnectionString).Password
$secureConnectionString = $null
環境變數會將 連接字串 排除在原始檔案之外,但程序及其子程序可以讀取它。 對於生產應用程式,盡可能使用 Microsoft Entra 認證,並在執行時從安全儲存庫擷取秘密。
建立應用程式
建立專案目錄並切換至該目錄:
使用下列程式碼建立名為
odbc-quickstart.cpp的檔案:#ifdef _WIN32 #include <windows.h> #endif #include <sql.h> #include <sqlext.h> #include <sqltypes.h> #include <cstdlib> #include <iomanip> #include <iostream> #include <string> std::string ReadEnvironmentVariable(const char* name) { #ifdef _WIN32 char* value = nullptr; std::size_t length = 0; if (_dupenv_s(&value, &length, name) != 0 || value == nullptr) return {}; std::string result(value); std::free(value); return result; #else const char* value = std::getenv(name); return value == nullptr ? std::string{} : value; #endif } void PrintDiagnostics(SQLSMALLINT handleType, SQLHANDLE handle) { SQLCHAR state[6]; SQLINTEGER nativeError; SQLCHAR message[SQL_MAX_MESSAGE_LENGTH]; SQLSMALLINT messageLength; for (SQLSMALLINT record = 1; SQL_SUCCEEDED(SQLGetDiagRec(handleType, handle, record, state, &nativeError, message, sizeof(message), &messageLength)); ++record) { std::cerr << '[' << state << "] (" << nativeError << ") " << message << '\n'; } } bool Succeeded(SQLRETURN result, SQLSMALLINT handleType, SQLHANDLE handle) { if (SQL_SUCCEEDED(result)) return true; PrintDiagnostics(handleType, handle); return false; } struct OdbcHandles { SQLHENV environment = SQL_NULL_HENV; SQLHDBC connection = SQL_NULL_HDBC; SQLHSTMT statement = SQL_NULL_HSTMT; ~OdbcHandles() { if (statement != SQL_NULL_HSTMT) SQLFreeHandle(SQL_HANDLE_STMT, statement); if (connection != SQL_NULL_HDBC) { SQLDisconnect(connection); SQLFreeHandle(SQL_HANDLE_DBC, connection); } if (environment != SQL_NULL_HENV) SQLFreeHandle(SQL_HANDLE_ENV, environment); } }; int main() { std::string connectionString = ReadEnvironmentVariable("ODBC_CONNECTION_STRING"); if (connectionString.empty()) { std::cerr << "Set ODBC_CONNECTION_STRING before running.\n"; return 1; } OdbcHandles handles; SQLRETURN result = SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &handles.environment); if (!SQL_SUCCEEDED(result)) { std::cerr << "Unable to allocate an ODBC environment handle.\n"; return 1; } result = SQLSetEnvAttr( handles.environment, SQL_ATTR_ODBC_VERSION, reinterpret_cast<SQLPOINTER>(SQL_OV_ODBC3_80), 0); if (!Succeeded(result, SQL_HANDLE_ENV, handles.environment)) return 1; result = SQLAllocHandle( SQL_HANDLE_DBC, handles.environment, &handles.connection); if (!Succeeded(result, SQL_HANDLE_ENV, handles.environment)) return 1; result = SQLDriverConnect( handles.connection, nullptr, reinterpret_cast<SQLCHAR*>(connectionString.data()), SQL_NTS, nullptr, 0, nullptr, SQL_DRIVER_NOPROMPT); if (!Succeeded(result, SQL_HANDLE_DBC, handles.connection)) return 1; result = SQLAllocHandle( SQL_HANDLE_STMT, handles.connection, &handles.statement); if (!Succeeded(result, SQL_HANDLE_DBC, handles.connection)) return 1; SQLINTEGER minimumProductId = 0; SQLLEN minimumProductIdLength = 0; result = SQLBindParameter( handles.statement, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 10, 0, &minimumProductId, 0, &minimumProductIdLength); if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement)) return 1; SQLCHAR query[] = "SELECT TOP (5) ProductID, Name " "FROM SalesLT.Product " "WHERE ProductID > ? " "ORDER BY ProductID;"; result = SQLExecDirect(handles.statement, query, SQL_NTS); if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement)) return 1; std::cout << "Product ID Name\n" << "---------- ----\n"; while (SQL_SUCCEEDED(result = SQLFetch(handles.statement))) { SQLINTEGER productId; SQLLEN productIdLength; SQLCHAR productName[256]; SQLLEN productNameLength; result = SQLGetData( handles.statement, 1, SQL_C_SLONG, &productId, sizeof(productId), &productIdLength); if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement)) return 1; result = SQLGetData( handles.statement, 2, SQL_C_CHAR, productName, sizeof(productName), &productNameLength); if (!Succeeded(result, SQL_HANDLE_STMT, handles.statement)) return 1; std::cout << std::left << std::setw(12) << productId << productName << '\n'; } if (result != SQL_NO_DATA) { PrintDiagnostics(SQL_HANDLE_STMT, handles.statement); return 1; } return 0; }
該應用程式僅使用標準的 ODBC API,因此包含驅動程式管理員標頭及驅動程式管理員函式庫的連結。 連線字串會在執行階段選擇 Microsoft ODBC Driver 18 for SQL Server。
SQL_DRIVER_NOPROMPT 阻止 SQLDriverConnect 開啟設定對話框。 若 連接字串 不完整,呼叫會回傳錯誤,應用程式會列印所有診斷記錄。
此查詢會將 0 綁定為 SQL Server 的 int 參數,讀取 SalesLT.Product 中前五項產品,並使用 SQLGetData 擷取每個產品的 ID 和名稱。
建置並執行應用程式
在同一個開發者 PowerShell 視窗中,編譯應用程式:
cl /std:c++17 /EHsc /W4 odbc-quickstart.cpp /link odbc32.lib執行應用程式:
.\odbc-quickstart.exe
應用程式的標準輸出為:
Product ID Name
---------- ----
680 HL Road Frame - Black, 58
706 HL Road Frame - Red, 58
707 Sport-100 Helmet, Red
708 Sport-100 Helmet, Black
709 Mountain Bike Socks, M
完成後清除連接字串。