C语言如何调用SQLServer数据库?

99ANYc3cd6
预计阅读时长 28 分钟
位置: 首页 C语言 正文

核心概念:ODBC (Open Database Connectivity)

在 C 语言中连接 SQL Server 最主流、最标准的方法是使用 ODBC API。

c语言调用sqlserver
(图片来源网络,侵删)
  • 什么是 ODBC? ODBC 是一个为访问数据库而标准的 API,它允许你的应用程序以统一的方式访问来自不同数据库供应商(如 SQL Server, Oracle, MySQL 等)的数据,你的 C 代码不需要关心数据库的具体实现细节,只需要调用标准的 ODBC 函数即可。

  • 工作流程:

    1. 应用程序:你的 C 程序。
    2. ODBC 驱动管理器:一个系统级别的库(在 Windows 上是 odbc32.dll),负责管理所有已安装的 ODBC 驱动程序。
    3. ODBC 驱动程序:针对特定数据库的驱动(SQL Server 的 ODBC 驱动),它知道如何与 SQL Server 进行通信。
    4. 数据源:一个逻辑名称,用于定义数据库的连接信息(服务器名、数据库名、用户名、密码等),应用程序通过数据源名称来连接数据库,而不需要硬编码所有连接信息。

第一步:准备工作

在编写代码之前,必须确保你的开发环境已经正确配置。

安装 SQL Server ODBC 驱动程序

你的开发机器上必须安装用于 SQL Server 的 ODBC 驱动程序,微软推荐使用最新的 ODBC Driver for SQL Server

c语言调用sqlserver
(图片来源网络,侵删)
  • 如何安装?
    • 访问微软官方下载页面:Microsoft ODBC Driver for SQL Server
    • 下载并安装适用于你操作系统(Windows 64/32位)的驱动程序,安装过程非常简单,基本上是“下一步”即可。

配置数据源 (可选,但推荐)

你可以选择通过 ODBC 数据源管理器来创建一个“数据源名称”(DSN),这样你的代码就只需要引用这个 DSN,而无需在代码中写明服务器、用户名等敏感信息。

  • 如何配置?
    1. 在 Windows 搜索中输入 “ODBC 数据源”,并打开 “ODBC 数据源管理器 (64位)”。
    2. 切换到 “系统 DSN” (System DSN) 选项卡。
    3. 点击 “添加...” 按钮。
    4. 在列表中选择你刚刚安装的 “ODBC Driver 17 for SQL Server” 或类似版本,然后点击 “完成”。
    5. 在弹出的配置窗口中:
      • 名称:给你的数据源起一个名字,MySqlServerDSN
      • 描述:(可选)填写描述。
      • 服务器:输入你的 SQL Server 实例名,如果是本地默认实例,可以写 localhost(local);如果是命名实例,写 localhost\实例名;IP 地址是 0.0.1,也可以直接写 IP。
    6. 点击 “下一步”,然后点击 “完成”。
    7. 在下一个窗口中,你可以选择 “使用 Windows 身份验证” 或 “使用 SQL Server 身份验证”,如果选择后者,需要输入用户名和密码。
    8. 点击 “测试数据源” 来确保连接成功。
    9. 最后点击 “确定” 保存。

你已经准备好了一个名为 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 或其他编译器,可能需要手动指定。

c语言调用sqlserver
(图片来源网络,侵删)

在链接时,你需要链接 odbc32.lib 库,在 Visual Studio 中,可以在项目属性 -> 链接器 -> 输入 -> 附加依赖项 中添加。

核心编程流程

  1. 分配环境句柄:初始化 ODBC 环境。
  2. 分配连接句柄:为连接到数据库做准备。
  3. 连接到数据源:使用 DSN 或连接字符串建立与 SQL Server 的连接。
  4. 分配语句句柄:准备执行 SQL 语句。
  5. 执行 SQL 语句:可以是查询(SELECT)、插入(INSERT)、更新(UPDATE)或删除(DELETE)。
  6. 处理结果集:如果是查询语句,需要遍历结果集并获取数据。
  7. 释放资源:按相反的顺序释放语句句柄、连接句柄和环境句柄。

第三步:完整代码示例

下面是一个完整的 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 为例)

  1. 创建一个新的 C++ 控制台项目(即使写 C 代码,C++ 项目也能编译通过)。
  2. 将上面的代码粘贴到 main.c 文件中。
  3. 右键点击项目 -> “属性”。
  4. 在 “配置属性” -> “链接器” -> “输入” -> “附加依赖项” 中,添加 odbc32.lib
  5. 确保代码中的 MySqlServerDSN、用户名和密码是正确的。
  6. 编译并运行。

第四步:现代替代方案

虽然 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。

-- 展开阅读全文 --
头像
织梦采集侠域名为何突然被锁定?
« 上一篇 今天
Linux C语言read函数如何使用?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]