核心概念:ODBC (Open Database Connectivity)
在 C 语言中连接 SQL Server 最主流、最标准的方法是使用 ODBC API。

-
什么是 ODBC? ODBC 是一个为访问数据库而标准的 API,它允许你的应用程序以统一的方式访问来自不同数据库供应商(如 SQL Server, Oracle, MySQL 等)的数据,你的 C 代码不需要关心数据库的具体实现细节,只需要调用标准的 ODBC 函数即可。
-
工作流程:
- 应用程序:你的 C 程序。
- ODBC 驱动管理器:一个系统级别的库(在 Windows 上是
odbc32.dll),负责管理所有已安装的 ODBC 驱动程序。 - ODBC 驱动程序:针对特定数据库的驱动(SQL Server 的 ODBC 驱动),它知道如何与 SQL Server 进行通信。
- 数据源:一个逻辑名称,用于定义数据库的连接信息(服务器名、数据库名、用户名、密码等),应用程序通过数据源名称来连接数据库,而不需要硬编码所有连接信息。
第一步:准备工作
在编写代码之前,必须确保你的开发环境已经正确配置。
安装 SQL Server ODBC 驱动程序
你的开发机器上必须安装用于 SQL Server 的 ODBC 驱动程序,微软推荐使用最新的 ODBC Driver for SQL Server。

- 如何安装?
- 访问微软官方下载页面:Microsoft ODBC Driver for SQL Server
- 下载并安装适用于你操作系统(Windows 64/32位)的驱动程序,安装过程非常简单,基本上是“下一步”即可。
配置数据源 (可选,但推荐)
你可以选择通过 ODBC 数据源管理器来创建一个“数据源名称”(DSN),这样你的代码就只需要引用这个 DSN,而无需在代码中写明服务器、用户名等敏感信息。
- 如何配置?
- 在 Windows 搜索中输入 “ODBC 数据源”,并打开 “ODBC 数据源管理器 (64位)”。
- 切换到 “系统 DSN” (System DSN) 选项卡。
- 点击 “添加...” 按钮。
- 在列表中选择你刚刚安装的 “ODBC Driver 17 for SQL Server” 或类似版本,然后点击 “完成”。
- 在弹出的配置窗口中:
- 名称:给你的数据源起一个名字,
MySqlServerDSN。 - 描述:(可选)填写描述。
- 服务器:输入你的 SQL Server 实例名,如果是本地默认实例,可以写
localhost或(local);如果是命名实例,写localhost\实例名;IP 地址是0.0.1,也可以直接写 IP。
- 名称:给你的数据源起一个名字,
- 点击 “下一步”,然后点击 “完成”。
- 在下一个窗口中,你可以选择 “使用 Windows 身份验证” 或 “使用 SQL Server 身份验证”,如果选择后者,需要输入用户名和密码。
- 点击 “测试数据源” 来确保连接成功。
- 最后点击 “确定” 保存。
你已经准备好了一个名为 MySqlServerDSN 的数据源。
第二步:C 语言编程步骤
下面是使用 ODBC API 在 C 语言中操作 SQL Server 的标准流程。
包含头文件和链接库
#include <windows.h> // Windows API #include <sql.h> // ODBC 核心头文件 #include <sqlext.h> // ODBC 扩展头文件 #include <stdio.h>
在 Visual Studio 中,这些头文件通常已经包含在 SDK 中,如果你使用 MinGW 或其他编译器,可能需要手动指定。

在链接时,你需要链接 odbc32.lib 库,在 Visual Studio 中,可以在项目属性 -> 链接器 -> 输入 -> 附加依赖项 中添加。
核心编程流程
- 分配环境句柄:初始化 ODBC 环境。
- 分配连接句柄:为连接到数据库做准备。
- 连接到数据源:使用 DSN 或连接字符串建立与 SQL Server 的连接。
- 分配语句句柄:准备执行 SQL 语句。
- 执行 SQL 语句:可以是查询(
SELECT)、插入(INSERT)、更新(UPDATE)或删除(DELETE)。 - 处理结果集:如果是查询语句,需要遍历结果集并获取数据。
- 释放资源:按相反的顺序释放语句句柄、连接句柄和环境句柄。
第三步:完整代码示例
下面是一个完整的 C 程序示例,它连接到 SQL Server,创建一张表,插入一条数据,然后查询并打印这条数据。
假设:
- 你已经按照上面的步骤配置了一个名为
MySqlServerDSN的系统 DSN。 - 你的 SQL Server 上有
testdb这个数据库,并且你对该数据库有操作权限。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
// 定义一个辅助函数来检查 ODBC 调用是否成功
#define CHECK_ERROR(func, handle, type) \
do { \
SQLRETURN ret = func; \
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { \
SQLCHAR sqlstate[6], msg[SQL_MAX_MESSAGE_LENGTH]; \
SQLSMALLINT len; \
SQLGetDiagRec(type, handle, 1, sqlstate, NULL, msg, sizeof(msg), &len); \
fprintf(stderr, "Error in %s at line %d: %s\n", #func, __LINE__, msg); \
goto cleanup; \
} \
} while(0)
int main() {
SQLHENV henv = SQL_NULL_HENV; // 环境句柄
SQLHDBC hdbc = SQL_NULL_HDBC; // 连接句柄
SQLHSTMT hstmt = SQL_NULL_HSTMT; // 语句句柄
SQLRETURN ret;
SQLCHAR connstr_out[1024]; // 连接字符串输出缓冲区
SQLSMALLINT connstr_out_len;
// 1. 分配环境句柄
ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
CHECK_ERROR(ret, henv, SQL_HANDLE_ENV);
// 设置 ODBC 版本为 3.80
ret = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
CHECK_ERROR(ret, henv, SQL_HANDLE_ENV);
// 2. 分配连接句柄
ret = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
CHECK_ERROR(ret, hdbc, SQL_HANDLE_ENV);
// 3. 连接到数据源
// 使用 DSN 连接
SQLCHAR dsn[] = "MySqlServerDSN";
SQLCHAR uid[] = "your_username"; // 如果使用 SQL Server 身份验证
SQLCHAR pwd[] = "your_password"; // 如果使用 SQL Server 身份验证
// 如果使用 Windows 身份验证,UID 和 PWD 设为空字符串
// ret = SQLDriverConnect(hdbc, NULL, dsn, SQL_NTS, connstr_out, sizeof(connstr_out), &connstr_out_len, SQL_DRIVER_COMPLETE);
// 如果使用 SQL Server 身份验证
ret = SQLConnect(hdbc, dsn, SQL_NTS, uid, SQL_NTS, pwd, SQL_NTS);
CHECK_ERROR(ret, hdbc, SQL_HANDLE_DBC);
printf("Successfully connected to SQL Server.\n");
// 4. 分配语句句柄
ret = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
CHECK_ERROR(ret, hstmt, SQL_HANDLE_DBC);
// 5. 执行 SQL 语句 - 创建表
char create_table_sql[] = "CREATE TABLE IF NOT EXISTS Employees (ID INT PRIMARY KEY, Name NVARCHAR(50), Age INT)";
ret = SQLExecDirect(hstmt, (SQLCHAR*)create_table_sql, SQL_NTS);
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
printf("Table might already exist or failed to create. Continuing...\n");
// 不 goto cleanup,继续执行
} else {
printf("Table 'Employees' created or already exists.\n");
}
// 执行 SQL 语句 - 插入数据
char insert_sql[] = "INSERT INTO Employees (ID, Name, Age) VALUES (1, 'John Doe', 30)";
ret = SQLExecDirect(hstmt, (SQLCHAR*)insert_sql, SQL_NTS);
CHECK_ERROR(ret, hstmt, SQL_HANDLE_STMT);
printf("Data inserted successfully.\n");
// 执行 SQL 语句 - 查询数据
char query_sql[] = "SELECT ID, Name, Age FROM Employees WHERE ID = 1";
ret = SQLExecDirect(hstmt, (SQLCHAR*)query_sql, SQL_NTS);
CHECK_ERROR(ret, hstmt, SQL_HANDLE_STMT);
printf("Query executed successfully.\n");
// 6. 处理结果集
SQLINTEGER id;
SQLWCHAR name[51]; // NVARCHAR 最多 50 个字符,需要 +1 的空间
SQLINTEGER age;
SQLLEN id_ind, name_ind, age_ind;
// 绑定列到变量
ret = SQLBindCol(hstmt, 1, SQL_C_LONG, &id, 0, &id_ind);
ret = SQLBindCol(hstmt, 2, SQL_C_WCHAR, name, sizeof(name), &name_ind);
ret = SQLBindCol(hstmt, 3, SQL_C_LONG, &age, 0, &age_ind);
printf("\n--- Query Results ---\n");
while (SQL_SUCCEEDED(ret = SQLFetch(hstmt))) {
// 注意:name 是 SQLWCHAR (Unicode),需要转换为 printf 能打印的 char
// 这里简单处理,实际情况可能需要更复杂的转换
wprintf(L"ID: %d, Name: %ls, Age: %d\n", id, name, age);
}
printf("--- End of Results ---\n");
// 7. 清理
cleanup:
if (hstmt != SQL_NULL_HSTMT) {
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
}
if (hdbc != SQL_NULL_HDBC) {
SQLDisconnect(hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
}
if (henv != SQL_NULL_HENV) {
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}
printf("\nProgram finished.\n");
system("pause"); // 暂停,以便查看控制台输出
return 0;
}
如何编译和运行 (以 Visual Studio 为例)
- 创建一个新的 C++ 控制台项目(即使写 C 代码,C++ 项目也能编译通过)。
- 将上面的代码粘贴到
main.c文件中。 - 右键点击项目 -> “属性”。
- 在 “配置属性” -> “链接器” -> “输入” -> “附加依赖项” 中,添加
odbc32.lib。 - 确保代码中的
MySqlServerDSN、用户名和密码是正确的。 - 编译并运行。
第四步:现代替代方案
虽然 ODBC 是非常强大和标准的,但对于新项目,你可能还有其他选择:
OLE DB
- 是什么:一个比 ODBC 更底层、性能更高的微软原生 API。
- 特点:直接面向数据源,不通过驱动管理器,速度更快,但 API 更复杂,使用也更繁琐。
- 适用场景:对性能要求极高的 C/C++ 应用。
ADO (ActiveX Data Objects)
- 是什么:一个基于 COM 的高级数据访问接口。
- 特点:使用起来比 ODBC 简单得多,语法更现代,但在纯 C 语言中使用比较麻烦,通常在 C++ 或更高级的语言(如 C#)中使用。
- 适用场景:Windows 平台下的 C++ 应用,尤其是已经使用 COM 技术的项目。
第三方库
- 是什么:如
libtds(FreeTDS 项目),它是 Sybase 和 Microsoft SQL Server 的协议开源实现。 - 特点:轻量级,不依赖 ODBC 驱动管理器,但你需要自己处理连接和协议细节。
- 适用场景:跨平台项目,或者希望避免微软官方依赖的环境。
总结与建议
| 方法 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
| ODBC | 标准、稳定、跨数据库、文档丰富 | API 较为繁琐,需要配置驱动和数据源 | Windows C/C++ 开发的首选和标准 |
| OLE DB | 性能高,底层直接访问 | API 复杂,仅限 Windows | 对性能有极致要求的 Windows C/C++ 应用 |
| ADO | 简单易用,高级抽象 | 主要面向 C++/COM,纯 C 使用困难 | Windows C++ 应用,尤其是现有 COM 项目 |
| 第三方库 | 轻量级,无 ODBC 依赖 | 需要自行维护,社区和文档可能不如 ODBC | 跨平台项目或特殊环境 |
对于绝大多数在 Windows 上使用 C 语言连接 SQL Server 的开发者来说,ODBC 是最可靠、最标准、资源最丰富的选择,请优先掌握和使用 ODBC。
