You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用C++ Poco库从SQL Server获取200万行数据时遇std::bad_alloc异常

解决Poco ODBC检索大数据量时的内存溢出问题

你遇到的std::bad_alloc是因为一次性把200万行数据全部加载到内存容器中,导致内存耗尽。在Poco框架下,有以下几种方法可以安全处理大数据量查询:

1. 逐行读取数据

直接利用Poco Statement的next()方法逐行获取结果,每次内存中仅保留一行数据,处理完成后即可释放。这种方式内存占用最低,适合超大规模数据集。

Poco::Data::ODBC::Connector::registerConnector();
Poco::Data::Session session("ODBC", "Driver={SQL Server};Server=localhost;Database=sample_db;UID=<username>;PWD=<pwd>");

std::string sql = "SELECT id, timestamp, type, int_value, float_value, string_value, bStatus "
                  "FROM sample_tbl "
                  "WHERE id = ? AND timestamp >= ? AND timestamp <= ?";

Poco::Data::Statement select(session);

// 用单个变量存储单条数据,而非容器
int id;
std::string timestamp;
int datatype;
Poco::Nullable<int> int_value;
Poco::Nullable<float> float_value;
std::string string_value;
bool status;

select << sql,
    Poco::Data::Keywords::into(id),
    Poco::Data::Keywords::into(timestamp),
    Poco::Data::Keywords::into(datatype),
    Poco::Data::Keywords::into(int_value),
    Poco::Data::Keywords::into(float_value),
    Poco::Data::Keywords::into(string_value),
    Poco::Data::Keywords::into(status),
    Poco::Data::Keywords::use(tagid),
    Poco::Data::Keywords::use(starttime),
    Poco::Data::Keywords::use(endtime);

select.execute();

// 循环逐行读取并处理
while (select.next()) {
    // 处理当前行数据:写入文件、计算、入库等
    // ...
}

2. 分批分页查询

借助SQL Server的ROW_NUMBER()窗口函数实现分页,每次查询固定行数的结果,循环处理所有批次。这种方式可以灵活控制每批数据的内存占用,适合需要批量处理的场景。

const int PAGE_SIZE = 100000; // 每批处理10万行
int current_page = 1;
bool has_more_data = true;

Poco::Data::ODBC::Connector::registerConnector();
Poco::Data::Session session("ODBC", "Driver={SQL Server};Server=localhost;Database=sample_db;UID=<username>;PWD=<pwd>");

while (has_more_data) {
    std::vector<int> ids;
    std::vector<std::string> timestamps;
    std::vector<int> datatypes;
    std::vector<Poco::Nullable<int>> int_values;
    std::vector<Poco::Nullable<float>> float_values;
    std::vector<std::string> string_values;
    std::vector<bool> bStatus;

    // 带分页逻辑的SQL
    std::string sql = "WITH PaginatedData AS ("
                      "    SELECT *, ROW_NUMBER() OVER (ORDER BY timestamp) AS RowNum "
                      "    FROM sample_tbl "
                      "    WHERE id = ? AND timestamp >= ? AND timestamp <= ?"
                      ") "
                      "SELECT id, timestamp, type, int_value, float_value, string_value, bStatus "
                      "FROM PaginatedData "
                      "WHERE RowNum BETWEEN ? AND ?";

    Poco::Data::Statement select(session);
    int start_row = (current_page - 1) * PAGE_SIZE + 1;
    int end_row = current_page * PAGE_SIZE;

    select << sql,
        Poco::Data::Keywords::into(ids),
        Poco::Data::Keywords::into(timestamps),
        Poco::Data::Keywords::into(datatypes),
        Poco::Data::Keywords::into(int_values),
        Poco::Data::Keywords::into(float_values),
        Poco::Data::Keywords::into(string_values),
        Poco::Data::Keywords::into(bStatus),
        Poco::Data::Keywords::use(tagid),
        Poco::Data::Keywords::use(starttime),
        Poco::Data::Keywords::use(endtime),
        Poco::Data::Keywords::use(start_row),
        Poco::Data::Keywords::use(end_row);

    select.execute();

    if (ids.empty()) {
        has_more_data = false;
    } else {
        // 处理当前批次数据
        // ...
        current_page++;
    }
}

3. 设置批量获取大小(Fetch Size)

通过Poco Statement的setFetchSize()方法,指定每次从ODBC驱动拉取的行数,让Poco分批次加载数据到内存。这种方式无需修改SQL,只需调整参数,平衡内存占用和查询效率。

Poco::Data::ODBC::Connector::registerConnector();
Poco::Data::Session session("ODBC", "Driver={SQL Server};Server=localhost;Database=sample_db;UID=<username>;PWD=<pwd>");

std::vector<int> ids;
std::vector<std::string> timestamps;
// ... 其他容器定义

std::string sql = "SELECT id, timestamp, type, int_value, float_value, string_value, bStatus "
                  "FROM sample_tbl "
                  "WHERE id = ? AND timestamp >= ? AND timestamp <= ?";

Poco::Data::Statement select(session);
select << sql,
    Poco::Data::Keywords::into(ids),
    Poco::Data::Keywords::into(timestamps),
    // ... 其他into和use绑定

// 设置每次从驱动拉取10万行
select.setFetchSize(100000);

select.execute();

// 处理已加载的批量数据
// ...

注意事项

  • 分页查询时,ORDER BY的字段建议创建索引,避免大页码时的性能损耗;
  • 逐行读取的效率相对较低,适合内存紧张但对性能要求不极致的场景;
  • setFetchSize的实际效果依赖ODBC驱动的支持,部分驱动可能会忽略该参数,需测试验证。

内容的提问来源于stack exchange,提问作者user29674386

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 10:25:18