版本:18.7.1.1
日期:2026年9月7日
要从 C 或 C++ 调用 ODBC API,请包含 sql.h、 sqlext.h和 sqltypes.h,然后链接驱动管理器的导入库。 要使用 Microsoft ODBC Driver for SQL Server 在 ODBC 标准之上添加的 SQL Server 扩展,还应包含 msodbcsql.h,并在核心 ODBC 头文件之后包含它。
适用于:Microsoft ODBC 驱动18版,适用于Windows、Linux和macOS上的SQL Server。 版本17使用相同的头部名称,包含 170 安装路径和 msodbcsql17 库名。
头文件和库
该平台提供核心的 ODBC 头部和驱动程序管理器,而非驱动程序包。 在 Windows 上,它们是随 Windows SDK 一起出厂的。 在 Linux 和 macOS 上,它们是随 unixODBC 开发包一起预发的。 驱动程序 SDK 仅提供 msodbcsql.h 和批量复制导入库。
| 你打的是什么 | Headers | Windows操作系统 | Linux | macOS |
|---|---|---|---|---|
| ODBC API |
sql.h、sqlext.h、sqltypes.h |
odbc32.lib |
-lodbc |
-lodbc |
| ODBC API,Unicode 入口点 | 添加 sqlucode.h |
odbc32.lib |
-lodbc |
-lodbc |
| ODBC 安装程序 API | 添加 odbcinst.h |
odbccp32.lib |
-lodbcinst |
-lodbcinst |
| SQL Server 驱动扩展 | 添加 msodbcsql.h |
没有额外的图书馆 | 没有额外的图书馆 | 没有额外的图书馆 |
批量复制(bcp_*)函数 |
添加 msodbcsql.h |
msodbcsql18.lib |
-lmsodbcsql-18 |
-lmsodbcsql.18 |
批量复制链接名称因平台不同而不同,因为文件名不同。 在 Linux 上, -lmsodbcsql-18 通过 libmsodbcsql-18.so 中的 /usr/lib符号链接解析,链接器已经在搜索该链接,所以你不需要 -L。 在 macOS 上,驱动程序随附为 libmsodbcsql.18.dylib,这与 -lmsodbcsql.18 相匹配,但在 Apple silicon 机型上,Homebrew 的库目录不在默认搜索路径中。 链接批量复制功能时添加 -L$(brew --prefix)/lib 。
只有批量复制函数需要驱动程序自己的库。 连接属性、语句属性、列属性和 SQL Server 类型标识符都是宏和类型定义,因此包含msodbcsql.h它们就足够了。
安装程序 API 是与 ODBC API 不同的独立库。 在 Linux 或 macOS 上,调用像 SQLGetPrivateProfileString 这样的函数而不使用 -lodbcinst 会在链接时因未定义引用而失败,而不是在编译时失败。
要安装提供Linux和macOS核心头部的unixODBC开发包,请参见 “安装UnixODBC驱动管理器”。
在 Linux 和 macOS 上的 C 代码中,在包含 msodbcsql.h 之前先包含 wchar.h
msodbcsql.h 的 Linux 和 macOS 版本使用 wchar_t 声明了 Always Encrypted 密钥存储提供程序接口,但它们不包含定义此类型的头文件。 在 C++ 中, wchar_t 是关键字,因此 C++ 翻译单元无需额外首部即可构建。 在C语言中, wchar_t 是typedef,所以你需要在C翻译单元中首先包含 <wchar.h> :
#include <wchar.h>
如果你未包含 <wchar.h>,编译器会报告来自 msodbcsql.h 内部的 unknown type name 'wchar_t' 错误。 在 Windows 上添加 include 是无害的,所以要把它加到共享源代码里,而不是放在平台守卫后面。
在核心 ODBC 头文件之后包含 msodbcsql.h
除msodbcsql.h定义的驱动程序名称宏之外的所有内容都位于#ifdef ODBCVER块中,而sql.h定义了ODBCVER。 如果你先包含msodbcsql.h,预处理器就会跳过整个代码块,而该头文件不会产生任何作用。 编译器不会发出警告。
/* Correct order. */
#ifdef _WIN32
#include <windows.h>
#endif
#include <wchar.h>
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include <msodbcsql.h>
在 sql.h 之前包含 msodbcsql.h 会使 ODBCVER 块内的所有内容都处于未定义状态。 编译器会在使用处报告该错误,而不是在包含处:
order-wrong.c(7): error C2065: 'SQL_COPT_SS_BCP': undeclared identifier
在 Windows 上,你必须在 ODBC 头部前包含windows.h。 Windows SDK 中的 sqltypes.h 和 sql.h 副本使用了 Windows 类型,例如 DWORD 和 LONG。
msodbcsql.h将其SQL Server结构包裹在pshpack8.h和poppack.h中。 没有 windows.h,构建会直接在 SDK 头文件本身中失败。
SDK文件安装地点
| 平台 | msodbcsql.h |
批量复制库 |
|---|---|---|
| Windows操作系统 | %ProgramFiles%\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include |
%ProgramFiles%\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Lib\<architecture>\msodbcsql18.lib |
| Linux | /opt/microsoft/msodbcsql18/include |
/opt/microsoft/msodbcsql18/lib64,带有一个 /usr/lib/libmsodbcsql-18.so 符号链接 |
| macOS | $(brew --prefix msodbcsql18)/include/msodbcsql18 |
$(brew --prefix)/lib/libmsodbcsql.18.dylib |
在 Windows 上,文件夹Lib包含安装程序放置的每种处理器架构的子文件夹,例如 x64、 x86、 arm64或 。 将 Include 文件夹添加到编译器的包含路径中,并将 architecture 子文件夹添加到链接器的库路径中。
在 Linux 上,共享对象带有版本号,命名类似于 libmsodbcsql-18.6.so.2.1,且不带有 SONAME。 该软件包会安装一个指向它的 /usr/lib/libmsodbcsql-18.so,这使得 -lmsodbcsql-18 在不使用 -L 选项的情况下也能解析。 通过该符号链接进行链接,而不是直接指定带版本号的文件,这样驱动程序更新时就不会导致构建失败。
在 macOS 上,Homebrew 会安装到其自身的前缀路径下:在 Apple silicon 上为 /opt/homebrew,在 Intel 机型上为 /usr/local。 这两个前缀都是指向带版本号的 Cellar 目录的符号链接。 在构建脚本中使用 brew --prefix msodbcsql18 和 brew --prefix unixodbc ,而不是硬编码其中任何一个。
路径中的数字对应驱动程序主版本号。 版本 17 在 Windows 上安装到 /opt/microsoft/msodbcsql17/,在 Linux 上安装到 ...\ODBC\170\SDK\,其导入库为 msodbcsql17.lib。
关于各平台的完整文件清单,请参见系统需求、安装和驱动程序文件(Windows)、在Linux上安装ODBC驱动,以及在macOS上安装ODBC驱动。
确认你的构建设置
该程序会根据首部编译,链接到驱动程序管理器,并列出驱动程序管理器能看到的驱动程序。 它无法连接,所以这把构建或注册问题和网络或凭证问题分开了。
#include <stdio.h>
#include <wchar.h>
#ifdef _WIN32
#include <windows.h>
#endif
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include <msodbcsql.h>
static void PrintDiagnostics(SQLSMALLINT handleType, SQLHANDLE handle)
{
SQLCHAR state[6];
SQLINTEGER native;
SQLCHAR message[SQL_MAX_MESSAGE_LENGTH];
SQLSMALLINT length;
for (SQLSMALLINT record = 1;
SQL_SUCCEEDED(SQLGetDiagRec(handleType, handle, record, state, &native,
message, sizeof(message), &length));
++record)
{
fprintf(stderr, " [%s] (%ld) %s\n", state, (long)native, message);
}
}
int main(void)
{
SQLHENV environment = SQL_NULL_HENV;
SQLRETURN rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &environment);
if (!SQL_SUCCEEDED(rc))
{
fprintf(stderr, "SQLAllocHandle for the environment failed.\n");
return 1;
}
rc = SQLSetEnvAttr(environment, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3_80, 0);
if (!SQL_SUCCEEDED(rc))
{
fprintf(stderr, "SQLSetEnvAttr for SQL_OV_ODBC3_80 failed.\n");
PrintDiagnostics(SQL_HANDLE_ENV, environment);
SQLFreeHandle(SQL_HANDLE_ENV, environment);
return 1;
}
printf("Driver name from msodbcsql.h: %s\n", SQLODBC_DRIVER_NAME);
printf("Installed drivers:\n");
SQLCHAR description[256];
SQLSMALLINT descriptionLength = 0;
SQLUSMALLINT direction = SQL_FETCH_FIRST;
while (SQL_SUCCEEDED(SQLDrivers(environment, direction,
description, sizeof(description), &descriptionLength,
NULL, 0, NULL)))
{
printf(" %s\n", description);
direction = SQL_FETCH_NEXT;
}
SQLFreeHandle(SQL_HANDLE_ENV, environment);
return 0;
}
把它做成一个狭义的角色程序。
SQLODBC_DRIVER_NAME 在定义了 UNICODE 或 _UNICODE 时会展开为宽字符串,而使用 %s 时 printf 无法接受这种字符串。
cl /W4 /I "%ProgramFiles%\Microsoft SQL Server\Client SDK\ODBC\180\SDK\Include" odbc-build-check.c /link odbc32.lib
在 Windows 上,/W4报告了来自 Windows SDK 中 C4201: nonstandard extension used: nameless struct/union 副本的两个 sqlext.h 警告。 这些警告来自SDK头部,而不是你的代码,构建是成功的。
第一行显示的是编译进你的二进制文件中的驱动程序名称。 其余的都是驱动管理器自己的列表,所以你预期看到但没看到的驱动是注册问题,不是构建问题。 你的列表会有所不同,包括所有已安装的ODBC驱动,而不仅仅是SQL Server驱动:
Driver name from msodbcsql.h: ODBC Driver 18 for SQL Server
Installed drivers:
SQL Server
ODBC Driver 17 for SQL Server
ODBC Driver 18 for SQL Server
Microsoft Access Driver (*.mdb, *.accdb)
Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)
Microsoft Access Text Driver (*.txt, *.csv)
Microsoft Access dBASE Driver (*.dbf, *.ndx, *.mdx)
使用 SQLODBC_DRIVER_NAME 构建连接字符串,而不是使用字符串字面量。 该宏会跟踪编译时所针对的头文件,因此升级 SDK 后,只需在一处即可更新驱动程序名称。
msodbcsql.h 对 ODBC API 的新增功能
msodbcsql.h扩展了标准 ODBC API 并包含 SQL Server 的具体内容。 每个家族占据一个连续的数值范围,从一个基数开始计算。 这些范围在不同族之间并不独特,所以你传递值给的函数就是区分它们的关键。
| 家庭 | 基常数 | Value |
|---|---|---|
SQLSetConnectAttr 的连接属性 |
SQL_COPT_SS_BASE |
1200 |
SQLSetStmtAttr 的语句属性 |
SQL_SOPT_SS_BASE |
1225 |
SQLColAttribute 的列属性 |
SQL_CA_SS_BASE |
1200 |
用于 SQLGetInfo 的信息类型 |
SQL_INFO_SS_FIRST |
1199 |
SQLGetDiagField 的诊断字段 |
SQL_DIAG_SS_BASE |
-1150 |
| 诊断动态功能代码 | SQL_DIAG_DFC_SS_BASE |
-200 |
该头文件还声明:
- 身份验证属性(包括
SQL_COPT_SS_AUTHENTICATION和SQL_COPT_SS_ACCESS_TOKEN)包含 Microsoft Entra ID 设置和访问令牌。 - 用于 ODBC 未定义的 SQL Server 类型的、范围为 -150 到 -199 的 SQL 类型标识符:
SQL_SS_VARIANT、SQL_SS_TIME2、SQL_SS_VECTOR、SQL_SS_UDT、SQL_SS_XML、SQL_SS_TABLE和SQL_SS_TIMESTAMPOFFSET。 这些表示一种 SQL 类型,因此,应在 ODBC 需要 SQL 类型的位置传入它们,例如SQLBindParameter的ParameterType参数。 - 缓冲区侧有三种匹配 的C 类型:
SQL_C_SS_TIME2、、SQL_C_SS_TIMESTAMPOFFSET和SQL_C_SS_VECTOR。 其他 SQL Server 类型绑定到标准的 ODBC C 类型,如SQL_C_BINARY或SQL_C_WCHAR,因此它们没有SQL_C_SS_*对应的。 -
SQL_C_SS_*类型绑定到的结构有:SQL_SS_TIME2_STRUCT、SQL_SS_TIMESTAMPOFFSET_STRUCT和SQL_SS_VECTOR_STRUCT。 - 批量复制原型和宏,包括
bcp_init、bcp_bind、bcp_sendrow、bcp_batchbcp_done和 。BCP_ENCRYPT_ON、BCP_ENCRYPT_OFF和BCP_ENCRYPT_STRICT选项仅在 Windows 标头中提供。
每个平台都随附自己的 msodbcsql.h,而且它们声明的符号并不完全相同。
SQLPERF 结构及用于填充该结构的性能连接属性(例如 SQL_COPT_SS_PERF_DATA 和 SQL_COPT_SS_PERF_QUERY)仅存在于 Windows 标头文件中。 Linux 和 macOS 的头部没有声明它们,驱动也不会收集这些平台的性能数据。 参见编程指南(Linux和macOS)。
对于这些属性对应的连接字符串关键字,请参见DSN和连接字符串关键字和属性。 有关 Microsoft Entra ID 设置,请参阅 将 Microsoft Entra ID 与 ODBC 驱动程序配合使用。 关于 向量 类型,请参见 向量数据类型。
在异步执行和线程之间选择
一些ODBC函数可以同步或异步运行。 在同步模式下,驱动程序直到服务器接听后才会返回控制权。 在异步模式下,驱动程序会立即返回 SQL_STILL_EXECUTING ,应用程序会用相同的参数重复同一调用,直到获得不同的返回码。 任何其他返回码,包括 SQL_ERROR,表示操作已完成。
异步模式有两种形式,你会使用其中一种。 使用 SQL_ASYNC_MODE 调用 SQLGetInfo,以确定驱动程序支持哪一种。 如果驱动程序支持按语句控制,则返回 SQL_AM_CONNECTION;如果该设置适用于整个连接,则返回 SQL_AM_NONE;如果驱动程序根本不以异步方式运行函数,则返回 SQL_AM_STATEMENT。
语句形式会对一个语句句柄启用异步模式。 连接中的每一个语句保持同步,因此你可以同时运行两种语句:
SQLSetStmtAttr(hStmt, SQL_ATTR_ASYNC_ENABLE,
(SQLPOINTER)SQL_ASYNC_ENABLE_ON, SQL_IS_INTEGER);
如果 SQL_ASYNC_MODE 返回 SQL_AM_CONNECTION,则该语句属性为只读,并且此次调用将返回 SQL_ERROR,SQLSTATE 为 HYC00。 改用连接表单吧。
连接表单会在你分配的每个语句句柄上开启异步模式。 它是否也会影响已存在的句柄取决于驱动程序,因此请在分配任何语句句柄之前先进行设置:
SQLSetConnectAttr(hDbc, SQL_ATTR_ASYNC_ENABLE,
(SQLPOINTER)SQL_ASYNC_ENABLE_ON, SQL_IS_INTEGER);
如果某个函数仍在该连接的某个语句上异步执行,则该调用返回 SQL_ERROR,并带有 SQLSTATE HY010。 光标开着本身不会阻止通话。 传递 SQL_ASYNC_ENABLE_OFF 会让连接上的每个语句回到同步模式。
要查找驱动程序在同一连接上同时支持多少异步语句,调用 SQLGetInfoSQL_MAX_ASYNC_CONCURRENT_STATEMENTS。 适用于 SQL Server 的 Microsoft ODBC Driver 18 返回 1,因此,应按每个连接只能有一个未完成的异步操作来规划;如果需要超过这一数量,则打开更多连接或使用线程。 参见异步执行(轮询方法)。
线程则是保持多个操作运行的另一种方式。 ODBC 要求多线程操作系统上的驱动程序必须是线程安全的,因此一个线程可以发出阻塞性的 ODBC 调用,而其他线程则继续工作。 这样可以避免异步模式所需的轮询循环和重复函数调用。 为每个线程分配各自的语句句柄。 驱动程序很可能会同时序列化两个使用相同句柄的线程,所以共享一个线程会让你失去并发性。 参见多线程。 优先使用线程处理新代码,并在转换已经运行的异步代码前先衡量自己的工作负载。
在 Windows 上,驱动管理器也支持通知方法,可以消除轮询循环。 你将 Win32 事件与连接或语句句柄关联。 该函数仍然会立即返回 SQL_STILL_EXECUTING ,驱动程序管理器在操作完成时会发出事件信号。 在此模式下,轮询已被禁用:再次调用原始函数会返回 SQL_ERROR,并带有 SQLSTATE IM017。 改为调用 SQLCompleteAsync 来获取结果。 这需要驱动管理器版本 ODBC 3.81 及以上版本,驱动也必须支持该版本。 打电话SQLGetInfoSQL_ASYNC_NOTIFICATION确认一下。 你获得的值取决于应用程序声明的 ODBC 版本:对于适用于 SQL Server 的 Microsoft ODBC Driver 18,将 SQL_ATTR_ODBC_VERSION 设置为 SQL_OV_ODBC3_80 的应用程序会得到 SQL_ASYNC_NOTIFICATION_CAPABLE,而声明 SQL_OV_ODBC3 的应用程序则会从同一驱动程序获得 SQL_ASYNC_NOTIFICATION_NOT_CAPABLE。 在分配连接之前,请先声明 SQL_OV_ODBC3_80。 参见 异步执行(通知方法) 和 通知方法示例。
取消一项未完成的行动
SQLCancel 取消仍在语句柄上运行的操作。 从另一个线程中调用它,或在轮询循环中调用它,并传入该未完成调用的句柄。
只用 SQLCancel 来做这个。 要放弃一个你不想再读取的结果集,请改为调用 SQLCloseCursor 或 SQLMoreResults。
从 sqlncli.h 迁移到 msodbcsql.h
SQL Server 原生客户端已停用,因此使用它的应用程序应迁移到适用于 SQL Server 的 Microsoft ODBC 驱动程序。 API 是同一个 ODBC API,所以大部分工作涉及重命名构建输入和 连接字符串 中的驱动名称。
| SQL Server Native Client | Microsoft ODBC Driver 18 for SQL Server |
|---|---|
sqlncli.h |
msodbcsql.h |
sqlncli11.lib |
msodbcsql18.lib |
sqlncli11.dll |
msodbcsql18.dll |
Driver={SQL Server Native Client 11.0} |
Driver={ODBC Driver 18 for SQL Server} |
SQLNCLI_VER |
SQLODBC_VER |
msodbcsql.h头部仍然定义SQLNCLI_*宏的名称,所以使用它们的源代码会继续编译。 这些定义由 #ifndef __sqlncli_h__ 保护,这意味着你不能在同一个翻译单元中同时包含这两个头文件。 移除 sqlncli.h include。
有两点是无法延续的:
- 返回链接服务器及其目录列表的分布式查询元数据 API 函数未在
msodbcsql.h中声明。 它们是针对SQL Server原生客户端的专用的。 - 版本18默认加密连接并验证服务器证书。 原生客户端则没有这样做。 一个在 Native Client 中可正常工作的连接字符串,在首次连接时可能会失败,直到你修复证书信任问题或显式设置
Encrypt。 参见 连接加密故障排除。
关于版本17到18的其他变更,请参见 主要版本差异。