C++实现SQL Server中的SQL查询结果集导出为Excel 2007格式的文件并设置合适的列宽度

在Visual Studio 2022中用C++和ODBC API在SQL Server数据库里实现将SQL查询语句的结果集用LibXL库导出为Excel 2007格式的文件,并且针对每一列的数据调整到合适宽度,并封装为函数。

将SQL查询结果导出为Excel并自动调整列宽的封装函数示例:

#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include <libxl.h>
#include <vector>
#include <string>
#include <algorithm>

using namespace libxl;

bool ExportSQLToExcel(const std::wstring& dsn,
                     const std::wstring& user,
                     const std::wstring& pwd,
                     const std::wstring& query,
                     const std::wstring& outputFile) {
    // 初始化ODBC句柄
    SQLHENV henv = SQL_NULL_HENV;
    SQLHDBC hdbc = SQL_NULL_HDBC;
    SQLHSTMT hstmt = SQL_NULL_HSTMT;

    // 分配环境句柄
    if (SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv) != SQL_SUCCESS)
        return false;

    // 设置ODBC版本
    SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);

    // 分配连接句柄
    if (SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc) != SQL_SUCCESS) {
        SQLFreeHandle(SQL_HANDLE_ENV, henv);
        return false;
    }

    // 连接数据源
    SQLRETURN ret = SQLConnect(hdbc,
        (SQLWCHAR*)dsn.c_str(), SQL_NTS,
        (SQLWCHAR*)user.c_str(), SQL_NTS,
        (SQLWCHAR*)pwd.c_str(), SQL_NTS);

    if (!SQL_SUCCEEDED(ret)) {
        SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
        SQLFreeHandle(SQL_HANDLE_ENV, henv);
        return false;
    }

    // 分配语句句柄
    if (SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt) != SQL_SUCCESS) {
        SQLDisconnect(hdbc);
        SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
        SQLFreeHandle(SQL_HANDLE_ENV, henv);
        return false;
    }

    // 执行查询
    if (SQLExecDirect(hstmt, (SQLWCHAR*)query.c_str(), SQL_NTS) != SQL_SUCCESS) {
        SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
        SQLDisconnect(hdbc);
        SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
        SQLFreeHandle(SQL_HANDLE_ENV, henv);
        return false;
    }

    // 获取列数
    SQLSMALLINT columnCount = 0;
    SQLNumResultCols(hstmt, &columnCount);
    if (columnCount == 0) {
        SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
        SQLDisconnect(hdbc);
        SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
        SQLFreeHandle(SQL_HANDLE_ENV, henv);
        return false;
    }

    // 获取列信息并初始化列宽存储
    std::vector<std::wstring> headers;
    std::vector<int> colWidths(columnCount, 0);

    for (SQLSMALLINT i = 0; i < columnCount; ++i) {
        SQLWCHAR colName[256];
        SQLSMALLINT nameLen;
        SQLDescribeCol(hstmt, i + 1, colName, 255, &nameLen, nullptr, nullptr, nullptr, nullptr);
        headers.push_back(colName);
        colWidths[i] = wcslen(colName); // 初始化列宽为标题长度
    }

    // 创建Excel对象
    Book* book = xlCreateXMLBook();
    if (!book) {
        SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
        SQLDisconnect(hdbc);
        SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
        SQLFreeHandle(SQL_HANDLE_ENV, henv);
        return false;
    }

    Sheet* sheet = book->addSheet(L"Sheet1");
    if (!sheet) {
        book->release();
        SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
        SQLDisconnect(hdbc);
        SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
        SQLFreeHandle(SQL_HANDLE_ENV, henv);
        return false;
    }

    // 写入标题行
    for (SQLSMALLINT i = 0; i < columnCount; ++i) {
        sheet->writeStr(0, i, headers[i].c_str());
    }

    // 处理数据行
    int row = 1;
    while (SQLFetch(hstmt) == SQL_SUCCESS) {
        for (SQLSMALLINT i = 0; i < columnCount; ++i) {
            SQLWCHAR buffer[1024];
            SQLLEN indicator;
            
            SQLRETURN ret = SQLGetData(hstmt, i + 1, SQL_C_WCHAR,
                                      buffer, sizeof(buffer), &indicator);

            if (SQL_SUCCEEDED(ret)) {
                if (indicator == SQL_NULL_DATA) {
                    sheet->writeStr(row, i, L"NULL");
                } else {
                    std::wstring value(buffer);
                    sheet->writeStr(row, i, value.c_str());
                    
                    // 更新列宽
                    int len = value.length();
                    if (len > colWidths[i]) {
                        colWidths[i] = len;
                    }
                }
            }
        }
        row++;
    }

    // 设置列宽(根据经验值调整)
    for (int i = 0; i < columnCount; ++i) {
        sheet->setCol(i, i, colWidths[i] * 1.2); // 宽度系数可调整
    }

    // 保存文件
    bool saveResult = book->save(outputFile.c_str());
    book->release();

    // 清理资源
    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
    SQLDisconnect(hdbc);
    SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
    SQLFreeHandle(SQL_HANDLE_ENV, henv);

    return saveResult;
}

使用示例:

int main() {
    bool result = ExportSQLToExcel(
        L"Your_DSN_Name",
        L"Your_Username",
        L"Your_Password",
        L"SELECT * FROM YourTable",
        L"output.xlsx"
    );

    if (result) {
        MessageBoxW(nullptr, L"导出成功!", L"信息", MB_OK);
    } else {
        MessageBoxW(nullptr, L"导出失败!", L"错误", MB_ICONERROR);
    }

    return 0;
}

需要注意以下事项:

  1. 环境配置

    • 需要安装ODBC驱动并配置数据源
    • 需要LibXL库(包含头文件和库文件)
    • 在项目属性中配置:
      • 附加包含目录:添加LibXL的include路径
      • 附加库目录:添加LibXL的lib路径
      • 链接器输入:添加libxl.lib
  2. 数据类型处理

    • 当前示例将所有数据转换为字符串处理
    • 可根据实际需求扩展数字、日期类型的特殊处理
  3. 列宽计算

    • 当前使用简单长度×1.2的系数
    • 可根据实际字体调整系数(通常1个字符≈1.5单位)
  4. 性能优化

    • 对于大数据量建议分页处理
    • 可添加进度回调函数
  5. 错误处理

    • 当前为简化示例使用了基本错误处理
    • 实际使用中应添加更详细的错误日志
  6. 编码问题

    • 确保数据库编码与程序编码一致(建议使用Unicode)
    • LibXL默认使用UTF-8编码

可根据具体需求扩展以下功能:

  • 添加样式设置(字体、颜色、边框等)
  • 支持多个工作表
  • 添加数据验证
  • 导出时显示进度
  • 更精确的列宽计算算法
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容