65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 ADO 在 C++ 中进行高效数据处理:GetRows 方法

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.03/5 (49投票s)

2005年6月27日

CPOL

8分钟阅读

viewsIcon

280107

downloadIcon

5752

演示通过 GetRows 方法提供的安全数组,在 C++ 中高效处理 ADO 记录集对象。

Sample Image - QuickADO.jpg

引言

该库是 C++ 应用程序通过 ADO 支持的安全数组处理 ADO 记录集的一种方式。它使用 ADO Recordset 接口的 GetRows 方法检索一块数据,并允许将其作为二维数组进行简单处理,类似于 Visual Basic 中的方式。

该库提供的优势

  1. 将 ADO 数据类型转换为 C++ 本机类型。该库还实现了二进制列的自动转换,使其转换为任何即用型格式,例如数据流、数据数组甚至可以直接显示的图像对象:IStreamISequentialStreamIPictureHBITMAP
  2. 它为 ADO 数据库应用程序可能实现的最快数据处理提供了一个平台。它对于需要运行复杂统计分析或进行其他数据处理的 ADO C++ 应用程序中的繁重计算非常高效。例如,它非常适合需要通过 ADO 进行繁重数据分析以生成在线客户端文档的后端应用程序。
  3. 该类通过选择性数据处理(即选择性列读取)实现了 C++ 中 ADO 的高效使用,从而节省了内存和 CPU 负载。
  4. 使用此库访问数据所需步骤大大减少,通过简单安全的 {row, col} 寻址逻辑即可访问值(无需使用 MoveNext 等命令遍历记录集或控制记录集游标位置)。
  5. 防错机制保证了数据处理过程中发生的任何问题都能通过类的 C++ 事件得到优雅处理。
  6. 隐藏了 ADO 安全数组的所有复杂性,这对于 C++ 开发人员来说是如此麻烦。使用此库也不需要使用 COM 智能指针来从 ADO 访问数据。

背景

在当今 Windows C++ 开发人员的世界中,数据库一词通常与两件事之一相关联:OLEDB 或 ADO,别介意我今天埋葬 DAO :) 我从我的大量实践中发现,大多数开发人员选择 ADO 而非 OLEDB,因为大多数数据库任务对速度的要求并不高,不足以证明 OLEDB 使用可能带来的时间延长,相比之下 ADO 更简单。简单性和时间范围通常决定了选择。多年来我在许多项目中使用 ADO,发现它是最实用的选择,除了当我的应用程序不够快时的一种假设情况——我从未遇到过这种情况。即使您遇到了,这个库也能让您大大扩展 ADO 的速度边界。

在本文中,我打算展示 ADO 还有另一个角度,只要稍加帮助,就能让 ADO 对追求数据处理速度和敏捷性的 C++ 开发人员来说更具吸引力。

ADO 提供三种数据访问方式

  1. 基于记录集的方法,大多数数据库开发人员都很熟悉,当我们通过将记录集游标移动到下一个位置来访问数据时;
  2. 安全数组,当可以从记录集订购一块数据以使用数组索引直接处理时;
  3. 通过 GetString 方法的字符串方法。

第一种方法一直都存在,对于本文而言,我们对此根本不感兴趣。第三种方法更适合脚本语言,其中更喜欢使用字符串。我一直在许多场合围绕第二种方法打转,好奇地想在我的 C++ 应用程序中以正确的方式使其工作。

您可以在以下文章中找到对所有三种数据读取方法的良好考虑。

ADO 安全数组的关键在于 ADO Recordset 接口的 GetRows 方法。它通过安全数组表示公开记录集数据,因此可以使用二维数组的简单索引逻辑来访问包含数据的单元格。ADO 的此安全数组功能是专门为在脚本语言(主要是 Visual Basic)中使用而创建的。事实上,如果我们上网搜索关于 GetRows 方法的信息,只会弹出 Visual Basic 的示例。为了使这项研究更全面,这里列出了一些您可能会遇到的链接

如果您查看这些示例,您会发现这种方法大多是优点而没有缺点。现在一个显而易见的问题应该是:为什么在 C++ 中没有人通过安全数组使用 ADO?答案很简单,原因在于此功能最初开发的初衷——Visual Basic。就是这样,没有为 C++ 提供,只要它符合 COM 自动化并在 VB 中工作——那就没问题了:) 为了澄清所有问题,简单地说,在 C++ 中使用多维安全数组是开发人员的噩梦。

当我在我的 C++ 应用程序中努力使用 ADO 安全数组时,我最终下定决心,走上了一条崎岖的道路,彻底改变这种状况,使其不再发生。我提供的这个库就是我探索的结果,完全开放供您评判。该库隐藏了安全数组、COM 智能指针和笨拙的光标操作的所有复杂性,使 ADO 在 C++ 中的使用从未如此简单、高效和优雅。

使用代码

整个库基本上只有一个类 CNCQuickADO,它完成了所有工作。它包含一个嵌入类 CColumns,用于管理要传递给 GetRows 方法的列的安全数组。在本文中,我尝试使用一种稍微不同的方法来记录该类,并将所有精力投入到头文件和实现文件的详细文档中。在文件 NCQuickADO.h 中,我为每个声明实体添加了大量注释,真的没有什么可以补充的了,所以我认为没有必要再尝试这样做。当然,我可能错了,所以我们拭目以待。对于那些懒得打开头文件的人,还有一个该类声明的 HTML 版本

您也会在演示应用程序中找到非常好的文档。演示应用程序(参见上面的截图)是一个简单的 ADO 客户端,它要求提供 ADO 连接字符串、SQL 查询,并在列表控件中显示该查询生成的所有数据。代码足够简单,此处无需额外注释,因此我将只强调主要用法概念。

您创建一个 ADO Recordset 对象,打开它,然后将其传递给 CNCQuickADO 类进行处理,如下例所示

// pRecordset is of type ADODB::_Recordset*
CNCQuickADO rs(pRecordset);
// That's it, all data soaked in, and object pRecordset
// can be released at this point;

还有四个可选参数也可以传递到类中(请参阅类声明)。让我们看更多一些将数据传递到类中的示例;

  • 只获取前 100 条记录
    // pRecordset is of type ADODB::_Recordset*
    CNCQuickADO rs(pRecordset, 100);
    // That's it, all 100 records have been read in,
    // and what happens with any remaining records is up to you;
  • 获取所有记录,但只从记录集中获取“ID”和“Name”列
    // pRecordset is of type ADODB::_Recordset*
    CNCQuickADO::CColumns cols(_T("ID"), _T("Name"), NULL);
    CNCQuickADO rs(pRecordset, 0, ADODB::adBookmarkCurrent, cols);
    // That's it, all records have been read in for the
    // selected columns, and object pRecordset
    // can be released at this point;

现在让我们考虑如何从类中访问数据。

  • 遍历所有记录,并从第一列读取 Long,从第五列读取 Text
    // rs is an initialized object of type CNCQuickADO;
    long nRows = rs.GetNumberRows(); // Get number of rows;
    for(long i = 0;i < nRows;i ++) // Go through all records;
    {
        long lValueID;
        _bstr_t strText;
        rs.GetLong(i, 0, lValue); // Read LONG from the first column;
        rs.GetText(i, 4, strText); // Read Text from the fifth column;
        // Process data here...
    }
  • 只读取所有记录的“Photo”列,它是一个位图文件
    // pRecordset is of type ADODB::_Recordset*
    CNCQuickADO::CColumns cols(_T("Photo"), NULL); // Column filter;
    CNCQuickADO rs(pRecordset, 0, ADODB::adBookmarkCurrent, cols);
    long nRows = rs.GetNumberRows(); // Get number of rows;
    for(long i = 0;i < nRows;i ++) // Go through all records;
    {
        HBITMAP hBitmap = (HBITMAP)rs.GetBinary(i, 0, bfBitmap);
        // Get ready-to-display bitmap;
    
        // Process the bitmap here...
    }

现在让我们考虑一个更复杂的计算示例,以展示通过 {行, 列} 对(类似于二维数组)访问值的有效性。假设我们有一个包含许多列的表,并且我们知道索引为 3、5 和 9 的列包含双精度类型的值(因为我们已经考虑了通过名称访问列的方式)。

所以我们想计算这些列的平均值之和。

// pRecordset is of type ADODB::_Recordset*
CNCQuickADO::CColumns cols(3L, 5, 9, -1); // Column filter;
CNCQuickADO rs(pRecordset, 0, ADODB::adBookmarkCurrent, cols);
long nRows = rs.GetNumberRows(); // Get number of rows;
long nCols = rs.GetNumberCols(); // Get number of columns;
double dAverageSum = 0.0; // Target value (sum of average values);
for(long i = 0;i < nRows;i ++) // Go through all records;
{
    double dAverage = 0.0; // Average value;
    for(long k = 0;k < nCols;k ++) // For all selected columns;
    {
        double dValue; // Temporary value;
        if(rs.GetDouble(i, k, dValue))
        // If retrieved the value successfully and it is not (NULL);
            dAverage += dValue;
    }
    dAverage /= nCols; // Row average;
    dAverageSum += dAverage; // Increment the sum of average values;
}

就这些了,如果您曾尝试在 C++ 中使用 GetRows 方法,那么您将能够体会到,多亏了这个库,所需操作的简单性。

因此,如果您觉得我的文章中缺少什么,请随时告诉我,我将尽快更新 ;) 我也感谢您对我的第一篇文章给予公正的评价和评论。谢谢。

关注点

在编写这段代码时,我不得不面对许多挑战。我想在这里列出几个对我来说最有趣的

  1. 设计和实现一个从动态数量的方法参数构造安全数组的类,即类 CNCQuickADO::CColumns。现在,无论何时您需要一个参数安全数组传递到其他地方,它都是一个非常可重用的代码片段。
  2. 让类 CNCQuickADO 正确处理并传递安全数组列到 ADO 的 GetRows 方法中。我找不到任何关于 GetRows 方法所需安全数组具体细节的文档,而且确实存在这样的细节。
  3. 正确处理调用 GetRows 方法时抛出异常可能发生的任何情况。在这种情况下缺乏文档让我不得不大量依赖实验。
  4. ADO 到变体数据类型的正确数据类型转换,就像 GetRows 方法所做的那样。没有任何转换表可用,所以我不得不自己逐一遍历所有现有的 ADO 数据类型,缓慢但确定。
  5. CNCQuickADO::GetBinary 方法的高效实现。我一直希望当列是图像文件时,这个方法能返回一个图像,所以现在我有了。

版权说明

此处发布的所有代码均由 www.neatcpp.com 提供,仅用于演示或学习目的,并按原样提供,不附带任何保证。

历史

我不能不在这里提一下,这是我在 CodeProject 上发表的第一篇文章。我一直想在过去的五六年里发表一篇,现在我终于开始动手,发表了一些我的代码。我希望它能像对我一样对其他开发人员有用。

  • 2005/06/27 - 初稿。
  • 2005/06/28 - 添加更多代码示例,润色英文。
  • 2005/07/06 - 根据 Walter Reiser 的建议,修改了库以支持 Oracle 数据库的 ADO 类型 adVarNumeric
© . All rights reserved.