在这个快速入门中,你需要在Windows、Linux或macOS上构建一个C++控制台应用程序。 该应用程序通过使用 Microsoft ODBC Driver 18 for SQL Server 连接到AdventureWorksLT数据库,绑定查询参数,执行查询并读取结果行。
先决条件
- Microsoft ODBC Driver 18 for SQL Server. 安装Windows、Linux或macOS的驱动。
- 一个 C++17 编译器以及该平台的 ODBC 开发文件:
- 在 Windows 上,安装 Visual Studio 2022 或 Visual Studio 2022 构建工具,配合 C++ 桌面开发工作负载。 Windows SDK 提供 ODBC 头文件和
odbc32.lib。 - 在Linux上,安装一个C++编译器和UnixODBC开发包作为你的发行版。 该软件包提供 ODBC 头文件和
libodbc。 - 在macOS上,安装Xcode命令行工具和Homebrew的unixODBC。
- 在 Windows 上,安装 Visual Studio 2022 或 Visual Studio 2022 构建工具,配合 C++ 桌面开发工作负载。 Windows SDK 提供 ODBC 头文件和
- 在Azure SQL 数据库或SQL Server中包含
AdventureWorksLT样本数据的数据库。 对于 Azure SQL 数据库,创建单个数据库时选择示例数据源。 对于 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,以便驱动程序验证服务器证书。 证书必须将服务器名称和链条与客户端信任的认证机构匹配。 有关配置指导,请参见证书验证失败。
Caution
TrustServerCertificate=yes 跳过证书验证。 在配置客户端信任的证书时,只用它进行局部独立开发。 请勿在生产环境中使用它。
设置环境变量时,不要将连接字符串添加到 Shell 历史记录中。
打开适用于 VS 2022 的开发人员 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
完成后,清除连接字符串。