客户端获取服务器端MySQL数据库表中数据C++实现 之前在 https://blog.csdn.net/fengbingchun/article/details/162538324 介绍过通过数据库图形化工具Beekeeper Studio实现在客户端访问服务器端(同一网段上的不同机子)MySQL数据库中表的操作流程并将表中数据保存到csv文件中这里通过MySQL API接口实现。这里使用的MySQL版本为8.0.19。安装完MySQL后在安装目录C:\Program Files\MySQL下有以下目录如下图所示这里使用MySQL Server 8.0中C API实现1. Connector C 8.0C开发用的客户端SDK可连接MySQL、执行SQL。此目录下有两套接口可实现连接MySQL(1).mysql\jdbc.h传统JDBC风格API但需依赖BoostMySQL高版本可能不再依赖Boost(2).mysqlx\xdevapi.hX DevAPIX Protocol端口不依赖Boost但需服务器端支持2. Connector ODBC 8.0ODBC(Open Database Connectivity)驱动让任何支持ODBC标准的应用程序都能连接MySQL。3. MySQL Router 8.0一个轻量级的中间层代理/路由组件。4. MySQL Server 8.0核心数据库服务端。也包含C API即mysql.h可用于连接MySQL不依赖Boost。5. MySQL Shell 8.0命令行管理工具。6. MySQL Workbench 8.0 CEMySQL图形化工具。用到的函数接口说明数据库中表会持续更新因此使用stmt系列接口1.mysql_init分配/初始化一个MYSQL连接对象失败返回nullptr。每个连接都必须先mysql_init用完后必须对应调用mysql_close。2.mysql_options设置连接选项比如连接超时(MYSQL_OPT_CONNECT_TIMEOUT)、读超时(MYSQL_OPT_READ_TIMEOUT)、写超时(MYSQL_OPT_WRITE_TIMEOUT)、字符集(MYSQL_SET_CHARSET_NAME)等成功返回0。必须在连接建立mysql_real_connect前调用才有效。3.mysql_real_connect连接数据库失败返回nullptr。失败时也需要调用mysql_close释放句柄。4.mysql_stmt_init创建Prepared Statement(预处理语句、预编译语句)对象失败返回nullptr。基于一个已连接的MYSQL对象创建一个新的预处理语句对象是使用mysql_stmt_*系列接口的第一步。MYSQL_STMT对象是绑定在具体连接上的连接断开重连后之前的stmt对象失效必须重新mysql_stmt_init mysql_stmt_prepare。5.mysql_stmt_prepare预处理SQL语句成功返回0。把带?占位符的SQL语句发给服务器预处理之后可以反复用不同参数值执行不需要每次都重新解析SQL。?的个数必须和mysql_stmt_bind_param绑定的参数个数一致。6.mysql_stmt_bind_param绑定输入参数成功返回0。把SQL语句中的?占位符和实际的C变量关联起来告诉MySQL每个?该用什么类型、从哪块内存取值。MYSQL_BIND数组长度必须等于SQL中?的个数且顺序要对应。7.mysql_stmt_execute执行预处理语句成功返回0。用当前绑定的参数值向服务器发送执行请求。执行成功后结果集还没有传输到客户端需要接下来mysql_stmt_bind_result mysql_stmt_fetch才能真正取到数据(除非调用了mysql_stmt_store_result那是一次性缓冲的做法流式方案不用它)。8.mysql_stmt_bind_result绑定输出结果成功返回0。告诉MySQL查询结果集里每一列应该存到哪块内存、用什么类型接收。和mysql_stmt_bind_param是对称的操作一个管输入参数一个管输出结果。必须在mysql_stmt_execute之后、mysql_stmt_fetch之前调用一次即可之后每次mysql_stmt_fetch都会往同一块内存里写新数据不需要重复mysql_stmt_bind_result。9.mysql_stmt_fetch逐行读取结果。从服务器取一行数据写入之前mysql_stmt_bind_result绑定的内存里。这是流式读取的核心每调用一次只取一行内存占用恒定不管结果集有多少行这也是不用mysql_stmt_store_result(效果等价于mysql_store_result一次性缓冲)的原因。返回值0成功取到一行MYSQL_NO_DATA结果集已经读完。10.mysql_error返回最近一次在这个连接上执行的API调用产生的错误描述文本。如果上一次操作没有出错返回空字符串。11.mysql_stmt_error和mysql_error类似但专门针对mysql_stmt_*系列函数的错误。12.mysql_stmt_close释放Prepared Statement对象成功返回0。每个mysql_stmt_init都要对应一次mysql_stmt_close尤其在重连场景下(旧连接断开时)要记得先关闭旧的stmt再重新mysql_stmt_init/mysql_stmt_prepare避免资源泄漏。13.mysql_close关闭与服务器的连接释放mysql_init分配的所有相关资源。如果这个连接上还有未关闭的MYSQL_STMT应该先mysql_stmt_close再mysql_close。测试代码如下执行此代码仅需要三个动态库libmysql.dll、libssl-1_1-x64.dll、libcrypto-1_1-x64.dll客户端不需要安装MySQLint test_client_mysql() { constexpr char host[]{ 192.168.19.205 }, user[]{ feo }, passwd[]{ 123456 }, db[]{ info }; constexpr unsigned int port{ 3306 }, timeout_sec{5}; constexpr char starttime[]{ 2026-06-01 00:00:00 }, endtime[]{ 2026-07-01 00:00:00 }; constexpr char csv_name[]{ feo.csv }; const std::string table_name{ time_values2 }, time_column{ receive_date }, value_column{ Feo }; auto conn mysql_init(nullptr); if (!conn) { std::cerr failed to mysql init std::endl; return -1; } if (auto ret mysql_options(conn, MYSQL_SET_CHARSET_NAME, utf8mb4); ret ! 0) { std::cerr failed to set charset name: mysql_error(conn) std::endl; return -1; } if (auto ret mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, timeout_sec); ret ! 0) { std::cerr failed to set connect timeout: mysql_error(conn) std::endl; return -1; } if (!mysql_real_connect(conn, host, user, passwd, db, port, nullptr, 0)) { std::cerr failed to connect: mysql_error(conn) std::endl; return -1; } auto stmt mysql_stmt_init(conn); if (!stmt) { std::cerr failed to stmt init: mysql_error(conn) std::endl; return -1; } std::string sql SELECT time_column , value_column FROM table_name WHERE time_column ? AND time_column ? ORDER BY time_column DESC; if (mysql_stmt_prepare(stmt, sql.c_str(), static_castunsigned long(sql.length())) ! 0) { std::cerr failed to stmp prepare: mysql_stmt_error(stmt) std::endl; mysql_stmt_close(stmt); return -1; } MYSQL_BIND param[2]{}; memset(param, 0, sizeof(param)); param[0].buffer_type MYSQL_TYPE_STRING; param[0].buffer (void*)starttime; param[0].buffer_length (unsigned long)strlen(starttime); param[1].buffer_type MYSQL_TYPE_STRING; param[1].buffer (void*)endtime; param[1].buffer_length (unsigned long)strlen(endtime); if (mysql_stmt_bind_param(stmt, param) ! 0) { std::cerr failed to stmt bind param: mysql_stmt_error(stmt) std::endl; return -1; } if (mysql_stmt_execute(stmt) ! 0) { std::cerr failed to stmt execute: mysql_stmt_error(stmt) std::endl; return -1; } char receive_date[32]{}, feo[32]{}; unsigned long date_len{ 0 }, feo_len{ 0 }; bool date_null{ 0 }, feo_null{ 0 }; MYSQL_BIND result[2]{}; memset(result, 0, sizeof(result)); result[0].buffer_type MYSQL_TYPE_STRING; result[0].buffer receive_date; result[0].buffer_length sizeof(receive_date); result[0].length date_len; result[0].is_null date_null; result[1].buffer_type MYSQL_TYPE_STRING; result[1].buffer feo; result[1].buffer_length sizeof(feo); result[1].length feo_len; result[1].is_null feo_null; if (mysql_stmt_bind_result(stmt, result) ! 0) { std::cerr failed to stmt bind result: mysql_stmt_error(stmt) std::endl; return -1; } std::ofstream csv(csv_name); if (!csv.is_open()) { std::cerr failed to open csv file: csv_name std::endl; return -1; } int count{ 0 }; while (true) { auto ret mysql_stmt_fetch(stmt); if (ret MYSQL_NO_DATA) break; if (ret 0) { receive_date[date_len] \0; feo[feo_len] \0; csv receive_date , feo \n; count; } else { std::cerr failed to fetch: ret , mysql_stmt_error(stmt) std::endl; } } std::cout number of valid data rows: count std::endl; mysql_stmt_close(stmt); mysql_close(conn); return 0; }结果如下GitHubhttps://github.com/fengbingchun/Messy_Test