【问题标题】:Implementing bulk record fetching实现批量记录获取
【发布时间】:2013-02-15 19:33:35
【问题描述】:

在我的程序开始时,我需要将 MS Access 数据库 (.mdb) 中的数据读取到下拉控件中。这样做是为了在用户输入该控件时,应用程序可以自动完成。

无论如何,从数据库中读取数据需要很长时间,所以我想我应该实现批量行获取。

这是我的代码:

CString sDsn;
CString sField;
sDsn.Format("ODBC;DRIVER={%s};DSN='';DBQ=%s",sDriver,sFile);
TRY
{
    // Open the database
    database.Open(NULL,false,false,sDsn);

    // Allocate the rowset
    CMultiRowset recset( &database );

    // Build the SQL statement
    SqlString =  "SELECT NAME "
            "FROM INFOTABLE";

    // Set the rowset size. These many rows will be fetched in one bulk operation
    recset.SetRowsetSize(25);

    // Open the rowset
    recset.Open(CRecordset::forwardOnly, SqlString, CRecordset::readOnly | CRecordset::useMultiRowFetch);

    // Loop through each rowset
    while( !recset.IsEOF() )
    {
        int rowsFetched = (int)recset.GetRowsFetched(); // This value is always 1 somehow
        for( int rowCount = 1; rowCount <= rowsFetched; rowCount++ )
        {
            recset.SetRowsetCursorPosition(rowCount);
            recset.GetFieldValue("NAME",sField);
            m_nameDropDown.AddString(sField);
        }

        // Go to next rowset
        recset.MoveNext();
    }

    // Close the database
    database.Close();
}
CATCH(CDBException, e)
{
    // If a database exception occured, show error msg
    AfxMessageBox("Database error: "+e->m_strError);
}
END_CATCH;

MultiRowset.cpp 看起来像:

#include "stdafx.h"
#include "afxdb.h"
#include "MultiRowset.h"

// Constructor
CMultiRowset::CMultiRowset(CDatabase *pDB)
   : CRecordset(pDB)
{
    m_NameData = NULL;
    m_NameDataLengths = NULL;

    m_nFields = 1;
    CRecordset::CRecordset(pDB);
}

void CMultiRowset::DoBulkFieldExchange(CFieldExchange *pFX)
{
   pFX->SetFieldType(CFieldExchange::outputColumn);
   RFX_Text_Bulk(pFX, _T("[NAME]"), &m_NameData, &m_NameDataLengths, 30);
}

MultiRowset.h 看起来像:

#if !defined(__MULTIROWSET_H_AD12FD1F_0566_4cb2_AE11_057227A594B8__)
#define __MULTIROWSET_H_AD12FD1F_0566_4cb2_AE11_057227A594B8__

class CMultiRowset : public CRecordset
{
public:
      // Field data members
      LPSTR m_NameData;

      // Pointers for the lengths of the field data
      long* m_NameDataLengths;

      // Constructor
      CMultiRowset(CDatabase *);

      // Methods
      void DoBulkFieldExchange(CFieldExchange *);
};

#endif

在我的数据库中,INFOTABLE 看起来像:

NAME    AGE
----    ---
Name1   Age1
Name2   Age2
      .
      .
      .
      .

我需要做的只是读取数据库中的数据。有人可以告诉我我做错了什么吗?我的代码现在的行为与正常提取完全一样。不会发生批量提取。

编辑:

我刚刚在DBRFX.cpp 中闲逛,发现RFX_Text_Bulk() 将我传递的m_NameData 初始化为new char[nRowsetSize * nMaxLength]

这意味着m_NameData 只是一个字符数组!我需要获取多个名称,所以我不需要二维字符数组吗?最奇怪的是,同样的RFX_Text_Bulk() 将我传递的m_NDCDataLengths 初始化为new long[nRowsetSize]。为什么一个字符数组需要一个长度数组?!

【问题讨论】:

  • 您的数据库中“[NAME]”字段的大小是多少?
  • @Goldorak84,最多 15 个字符。
  • 其实m_NameData代表一个字符数组的数组。 m_NDCDataLengths 表示 m_NameData 中每个字符串的长度
  • @Goldorak84,但m_nameData 被初始化为new char[nRowsetSize * nMaxLength];。这不是使它成为长度为nRowsetSize * nMaxLength的字符数组吗?
  • CMultiRowset 构造函数有问题。您应该删除 CRecordset::CRecordset(pDB);在函数的最后。它可能会将 m_nFields 重置为 0

标签: c++ database mfc fetch bulk


【解决方案1】:

根据http://msdn.microsoft.com/en-us/library/77dcbckz.aspx#_core_how_crecordset_supports_bulk_row_fetching,您必须在调用 SetRowsetSize 之前使用 CRecordset::useMultiRowFetch 标志打开 CRecordset:

要实现批量行提取,您必须指定 的 dwOptions 参数中的 CRecordset::useMultiRowFetch 选项 打开成员函数。要更改行集大小的设置,请调用 设置行集大小。

【讨论】:

  • 那个链接好像坏了。同样来自this MSDN articleAfter you have initialized the rowset size, call the Open member function. Here you must specify the CRecordset::useMultiRowFetch option。这似乎是矛盾的。
  • 我已经修复了链接。是的,你是对的。尝试检查是否实现了批量提取 - 在 SetRowsetSize 之前调用 GetRowsetSize: {quote} 在打开记录集对象之前,您可以使用 SetRowsetSize 成员函数定义行集大小。行集大小指定在一次提取期间应检索多少条记录。实现批量取行时,默认行集大小为 25。如果未实现批量取行,行集大小保持固定为 1。{quote}
  • 行集大小似乎设置正确。我目前将其设置为 25。 GetRowsetSize() 也返回 25。
【解决方案2】:

你几乎猜对了。要获取值, 我会改变你的

        for( int rowCount = 1; rowCount <= rowsFetched; rowCount++ )
        {
            recset.SetRowsetCursorPosition(rowCount);
            recset.GetFieldValue("NAME",sField);
            m_nameDropDown.AddString(sField);
        }

通过这样的方式

for( int nPosInRowset = 0; nPosInRowset < rowsFetched; nPosInRowset++ )
{
    //Check if value is null
    if (*(recset.m_NameDataLengths + nPosInRowset) == SQL_NULL_DATA)
        continue;    

    CString csComboString;
    csComboString = (recset.m_NameData + (nPosInRowset * 30)); //Where 30 is the size specified in RFX_Text_Bulk

    m_nameDropDown.AddString(csComboString);
}

编辑:要获取多于一行,请删除 CRecordset::forwardOnly 选项

编辑 2:您也可以保留 CRecordset::forwardonly,但添加 CRecordset::useExtendedFetch 选项

【讨论】:

  • 我确实试过这个。问题是,rowsFetched 始终为 1!
  • 你连接的是什么类型的数据库?
  • 我编辑了我的帖子:要获取多行,请删除 CRecordset::forwardOnly 选项
  • 使用 LPSTR * 会更合乎逻辑,但如果我们寻求性能,只分配一个新的 char[nRowsetSize * nMaxLength] 而不是 (nRowsetSize) 乘以新的 char[nMaxLength] 会更快.如果您使用上面提供的代码,您应该能够填写您的组合框。这能解决您的问题吗?
  • 好的,我刚刚将您的 CString 分配修改为 csComboString.Format("%s", (recset.m_NameData + (nPosInRowset * 30))); 和宾果游戏!这就像一个魅力。非常感谢!
【解决方案3】:

刚刚遇到同样的问题。 您应该在recset.Open() 调用中仅使用dwOptions 参数CRecordset::useMultiRowFetch,而不是CRecordset::readOnly | CRecordset::useMultiRowFetch。 希望这对某人有所帮助...

编辑:- 重新检查后是这种情况-当使用批量记录集并以CRecordset::forwardOnlyCRecordset::readOnly 打开时,您还必须在dwOptions 中指定CRecordset::useExtendedFetch。对于其他类型的滚动,使用CRecordset::readOnly | CRecordset::useMultiRowFetch 就可以了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-06-04
    • 1970-01-01
    • 2018-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-06
    相关资源
    最近更新 更多