在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;
}
需要注意以下事项:
-
环境配置:
- 需要安装ODBC驱动并配置数据源
- 需要LibXL库(包含头文件和库文件)
- 在项目属性中配置:
- 附加包含目录:添加LibXL的include路径
- 附加库目录:添加LibXL的lib路径
- 链接器输入:添加libxl.lib
-
数据类型处理:
- 当前示例将所有数据转换为字符串处理
- 可根据实际需求扩展数字、日期类型的特殊处理
-
列宽计算:
- 当前使用简单长度×1.2的系数
- 可根据实际字体调整系数(通常1个字符≈1.5单位)
-
性能优化:
- 对于大数据量建议分页处理
- 可添加进度回调函数
-
错误处理:
- 当前为简化示例使用了基本错误处理
- 实际使用中应添加更详细的错误日志
-
编码问题:
- 确保数据库编码与程序编码一致(建议使用Unicode)
- LibXL默认使用UTF-8编码
可根据具体需求扩展以下功能:
- 添加样式设置(字体、颜色、边框等)
- 支持多个工作表
- 添加数据验证
- 导出时显示进度
- 更精确的列宽计算算法