Home All Groups Group Topic Archive Search About

10 seconds for MoveFirst on a rowset?

Author
11 Jan 2006 6:02 PM
Roger Garrett
I'm doing a query on a database that has 250,000 records. The query is
returning a rowset with just 12 records in it. When I do the MoverFirst() on
the rowset it takes 10 seconds to accomplish. And when I do subsequent calls
to MoveNext() on the rowset it takes anywhere from 2 to 12 seconds to
accomplish the move.

This doesn't seem reasonable and I'm at a loss as to what I might be doing
wrong. When I was using a database with just a few thousand records the calls
to MoveFirst() and MoveNext() on the returned recordsets went incredibly
fast. But now that I'm using a huge database they'e slowed down to a crawl.
But why should there be any relationship between the size of the database and
the speed with which a returned recordset can be traversed?

Does anyone have any insight into this?

Roger Garrett

Author
11 Jan 2006 6:29 PM
JT
I'm guessing, but are you talking about returning the rowset to the
application via ADO? The following article describes the use of
CursorLocation (adUseServer, adUseClient), CursorType (adOpenForwardOnly,
adOpenKeyset, adOpenDynamic, adOpenStatic), and LockType (adLockReadOnly,
adLockPessimistic, adLockOptimistic, adLockBatchOptimistic), and how it
impacts performance depending on the situation.
http://www.sqlteam.com/item.asp?ItemID=11842
If you are basically just needing to pass the rowset to something like
Crystal Reports or Excel for reporting purposes, then what you want is a
adOpenForwardOnly / adLockReadOnly or "firehose" cursor.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ado270/htm/mdprocursortype.asp
http://www.microsoft.com/resources/documentation/Windows/2000/server/reskit/en-us/Default.asp?url=/resources/documentation/Windows/2000/server/reskit/en-us/iisbook/c07_recordsets_and_cursors.asp
With 250,000 rows, you may want an adUseServer cursor with a reasonable
sized CacheSize setting.
http://msdn.microsoft.com/library/en-us/ado270/htm/mdprocachesize.asp


Show quote
"Roger Garrett" <RogerGarr***@discussions.microsoft.com> wrote in message
news:9EB8E5B4-6D1F-45BB-B36F-CA8F90E8F58E@microsoft.com...
> I'm doing a query on a database that has 250,000 records. The query is
> returning a rowset with just 12 records in it. When I do the MoverFirst()
> on
> the rowset it takes 10 seconds to accomplish. And when I do subsequent
> calls
> to MoveNext() on the rowset it takes anywhere from 2 to 12 seconds to
> accomplish the move.
>
> This doesn't seem reasonable and I'm at a loss as to what I might be doing
> wrong. When I was using a database with just a few thousand records the
> calls
> to MoveFirst() and MoveNext() on the returned recordsets went incredibly
> fast. But now that I'm using a huge database they'e slowed down to a
> crawl.
> But why should there be any relationship between the size of the database
> and
> the speed with which a returned recordset can be traversed?
>
> Does anyone have any insight into this?
>
> Roger Garrett
>
Author
11 Jan 2006 9:28 PM
Roger Garrett
I'm not sure whether this is ADO or not. I'm a beginner at this and been
using some examples that I found on the Internet and in some books. I have
the following two classes:

class CRowsetAccessorMembersGuidUrlOnlineServabilityConnection
{
public:
    char m_strMemberGuid[CGuidMember::GUID_LENGTH_STRING + 1];
    char m_strUrl[15 + 1];
    short m_IsOnline;
    short m_IsServable;
    short m_ConnectionType;

    BEGIN_COLUMN_MAP(CRowsetAccessorMembersGuidUrlOnlineServabilityConnection)
        COLUMN_ENTRY(1, m_strMemberGuid)
        COLUMN_ENTRY(2, m_strUrl)
        COLUMN_ENTRY(3, m_IsOnline)
        COLUMN_ENTRY(4, m_IsServable)
        COLUMN_ENTRY(5, m_ConnectionType)
    END_COLUMN_MAP()

    void ClearRecord()
    {
        memset(this, 0, sizeof(*this));
    };
};

class CRowsetMembersGuidUrlOnlineServabilityConnection : public
CCommand<CAccessor<CRowsetAccessorMembersGuidUrlOnlineServabilityConnection> >
{
public:

    HRESULT Open(CSession& Session) // using the default Query
    {
        CDBPropSet propset(DBPROPSET_ROWSET);
        propset.AddProperty(DBPROP_IRowsetChange, true);
        propset.AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE |
            DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE);
        return
CCommand<CAccessor<CRowsetAccessorMembersGuidUrlOnlineServabilityConnection>
>::Open(Session, NULL, &propset);
    };

    HRESULT Open(CSession& Session, CString strQuery) // using a specified Query
    {
        CDBPropSet propset(DBPROPSET_ROWSET);
        propset.AddProperty(DBPROP_IRowsetChange, true);
        propset.AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE |
            DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE);
        return
CCommand<CAccessor<CRowsetAccessorMembersGuidUrlOnlineServabilityConnection>
>::Open(Session, (LPCSTR)strQuery, &propset);
    };
};


and in my code, after I've set up the query string, I do the following to
get the rowset and then handle the data in the rowset:

CRowsetMembersGuidUrlOnlineServabilityConnection
RowsetMemberGuidUrlOnlineServabilityConnection;
hResult = RowsetMemberGuidUrlOnlineServabilityConnection.Open(m_Session,
strQuery);

if (SUCCEEDED(hResult))
{
    hResult = RowsetMemberGuidUrlOnlineServabilityConnection.MoveFirst(); //
will fail if no records found
    if (SUCCEEDED(hResult) && (hResult != DB_S_ENDOFROWSET))
    {
        // We actually have some matches.
        BOOL bHaveARecord;
        bHaveARecord = TRUE;
        while (bHaveARecord)
        {
            // Extract the data from the current record
            // MEMBER GUID
            CString strMemberGuid =
RowsetMemberGuidUrlOnlineServabilityConnection.m_strMemberGuid;
            // URL
            CString strUrl = RowsetMemberGuidUrlOnlineServabilityConnection.m_strUrl;
            // IS ONLINE
            BOOL bIsOnline = FALSE;
            bIsOnline =  (RowsetMemberGuidUrlOnlineServabilityConnection.m_IsOnline
== 1);
            // IS SERVABLE
            BOOL bIsServable = FALSE;
            bIsServable =
(RowsetMemberGuidUrlOnlineServabilityConnection.m_IsServable == 1);
            // CONNECTION TYPE
            CConnectionTypes::CONNECTION_TYPE ConnectionType =
CConnectionTypes::CONNECTION_TYPE_INVALID;
            ConnectionType =
(CConnectionTypes::CONNECTION_TYPE)RowsetMemberGuidUrlOnlineServabilityConnection.m_ConnectionType;

            so some operations with the data....

    hResult = RowsetMemberGuidUrlOnlineServabilityConnection.MoveNext();
            bHaveARecord = (SUCCEEDED(hResult) && (hResult != DB_S_ENDOFROWSET));
        }
    }
}

The calls to MoveFirst and MoveNext take many seconds to complete, which
just doesn't seem right to me, especially since the actual query takes much
less than a second.

What can I do to speed it up?

Roger Garrett
Author
12 Jan 2006 12:52 AM
JT
As a programming language, C++ seems so stilted compared to my day to day VB
or T-SQL, but I'll give it a try. Some of the method calls resemble ADO, but
I don't recognize the object names like
CRowsetAccessorMembersGuidUrlOnlineServabilityConnection, and there was no
hit on google either. Perhaps this is a 3rd party or in-house developed data
access class or high level "wrapper" class for the ADO connection and
recordset objects. I would reccomend contacting whomever provided you with
the data access class and describe the problem with the latency when
selecting data or attempting a move to a different row; there is no telling
how they have it programmed internally.


Show quote
"Roger Garrett" <RogerGarr***@discussions.microsoft.com> wrote in message
news:FAED42A0-3B25-44A0-B95B-7AEDAFF0243B@microsoft.com...
> I'm not sure whether this is ADO or not. I'm a beginner at this and been
> using some examples that I found on the Internet and in some books. I have
> the following two classes:
>
> class CRowsetAccessorMembersGuidUrlOnlineServabilityConnection
> {
> public:
>  char m_strMemberGuid[CGuidMember::GUID_LENGTH_STRING + 1];
>  char m_strUrl[15 + 1];
> short m_IsOnline;
> short m_IsServable;
>  short m_ConnectionType;
>
> BEGIN_COLUMN_MAP(CRowsetAccessorMembersGuidUrlOnlineServabilityConnection)
>  COLUMN_ENTRY(1, m_strMemberGuid)
>  COLUMN_ENTRY(2, m_strUrl)
>  COLUMN_ENTRY(3, m_IsOnline)
>  COLUMN_ENTRY(4, m_IsServable)
>  COLUMN_ENTRY(5, m_ConnectionType)
> END_COLUMN_MAP()
>
> void ClearRecord()
> {
> memset(this, 0, sizeof(*this));
> };
> };
>
> class CRowsetMembersGuidUrlOnlineServabilityConnection : public
> CCommand<CAccessor<CRowsetAccessorMembersGuidUrlOnlineServabilityConnection>
>  >
> {
> public:
>
> HRESULT Open(CSession& Session) // using the default Query
> {
> CDBPropSet propset(DBPROPSET_ROWSET);
> propset.AddProperty(DBPROP_IRowsetChange, true);
> propset.AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE |
> DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE);
> return
> CCommand<CAccessor<CRowsetAccessorMembersGuidUrlOnlineServabilityConnection>
>>::Open(Session, NULL, &propset);
> };
>
> HRESULT Open(CSession& Session, CString strQuery) // using a specified
> Query
> {
> CDBPropSet propset(DBPROPSET_ROWSET);
> propset.AddProperty(DBPROP_IRowsetChange, true);
> propset.AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE |
> DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE);
> return
> CCommand<CAccessor<CRowsetAccessorMembersGuidUrlOnlineServabilityConnection>
>>::Open(Session, (LPCSTR)strQuery, &propset);
> };
> };
>
>
> and in my code, after I've set up the query string, I do the following to
> get the rowset and then handle the data in the rowset:
>
> CRowsetMembersGuidUrlOnlineServabilityConnection
> RowsetMemberGuidUrlOnlineServabilityConnection;
> hResult = RowsetMemberGuidUrlOnlineServabilityConnection.Open(m_Session,
> strQuery);
>
> if (SUCCEEDED(hResult))
> {
> hResult = RowsetMemberGuidUrlOnlineServabilityConnection.MoveFirst(); //
> will fail if no records found
> if (SUCCEEDED(hResult) && (hResult != DB_S_ENDOFROWSET))
> {
> // We actually have some matches.
> BOOL bHaveARecord;
> bHaveARecord = TRUE;
> while (bHaveARecord)
> {
> // Extract the data from the current record
> // MEMBER GUID
> CString strMemberGuid =
> RowsetMemberGuidUrlOnlineServabilityConnection.m_strMemberGuid;
> // URL
> CString strUrl = RowsetMemberGuidUrlOnlineServabilityConnection.m_strUrl;
> // IS ONLINE
> BOOL bIsOnline = FALSE;
> bIsOnline =  (RowsetMemberGuidUrlOnlineServabilityConnection.m_IsOnline
> == 1);
> // IS SERVABLE
> BOOL bIsServable = FALSE;
> bIsServable =
> (RowsetMemberGuidUrlOnlineServabilityConnection.m_IsServable == 1);
> // CONNECTION TYPE
> CConnectionTypes::CONNECTION_TYPE ConnectionType =
> CConnectionTypes::CONNECTION_TYPE_INVALID;
> ConnectionType =
> (CConnectionTypes::CONNECTION_TYPE)RowsetMemberGuidUrlOnlineServabilityConnection.m_ConnectionType;
>
> so some operations with the data....
>
> hResult = RowsetMemberGuidUrlOnlineServabilityConnection.MoveNext();
> bHaveARecord = (SUCCEEDED(hResult) && (hResult != DB_S_ENDOFROWSET));
> }
> }
> }
>
> The calls to MoveFirst and MoveNext take many seconds to complete, which
> just doesn't seem right to me, especially since the actual query takes
> much
> less than a second.
>
> What can I do to speed it up?
>
> Roger Garrett
>
Author
12 Jan 2006 4:05 PM
Roger Garrett
JT,

The names like CRowsetAccessorMembersGuidUrlOnlineServabilityConnection are
my names for my classes. They are built on the CAccessor and CCommand
templates of Microsoft's OLE DB Consumer Templates Except for the data
members, they're basically copies of examples from Microsoft.

CRowsetMembersGuidUrlOnlineServabilityConnection defines the data members,
and CRowsetAccessorMembersGuidUrlOnlineServabilityConnection defines the
accessor to the rowset.

I declare an object of type CRowsetGuidUrlServabillityConnectionType (which
is of type CCommand) and then use the open() method on that object, passing
in the dastabase session and the query string. That performs the query and
fills the RowsetGuidUrlServabillityConnectionType object with the results. I
can then use that object's MoveFIrst() and MoveNext() to retrieve the
individual records.

CRowsetGuidUrlServabillityConnectionType
RowsetGuidUrlServabillityConnectionType;
        HRESULT hResult;
//TRACE("DB -->RowsetGuidUrlServabillityConnectionType.Open\n");
        hResult = RowsetGuidUrlServabillityConnectionType.Open(m_Session, strQuery);


I guess I need someone who understands the CAccessor and CCommand templates.
Author
12 Jan 2006 5:15 PM
Jim Underwood
From the sounds of it this is an application code issue and not a DB issue
at all.  I am betting you can change your application code and correct the
problem fairly quickly.

First thing I would recomend is do a quick performance test by removing thr
ereferences to your classes and directly using the .Net classes that are
provided.  You have far too much going on here to readily find the source of
your problems.

Just execute your SQL Query or Procedure and return a dataset.  Loop through
that dataset and pop up a message box showing the key field for each row.
Do this with the simplest, least amount of code possible, and see how long
it takes.  I am betting you will see each row processed in milliseconds or
less.

Try this, then you can strat putting your functions into the code a small
piece at a time and retesting in the same manner.  You'll know right away
when you add logic that slows things down.


Show quote
"Roger Garrett" <RogerGarr***@discussions.microsoft.com> wrote in message
news:0A4184A8-7B4E-4CBF-B2C6-4C228ED0F7EC@microsoft.com...
> JT,
>
> The names like CRowsetAccessorMembersGuidUrlOnlineServabilityConnection
are
> my names for my classes. They are built on the CAccessor and CCommand
> templates of Microsoft's OLE DB Consumer Templates Except for the data
> members, they're basically copies of examples from Microsoft.
>
> CRowsetMembersGuidUrlOnlineServabilityConnection defines the data members,
> and CRowsetAccessorMembersGuidUrlOnlineServabilityConnection defines the
> accessor to the rowset.
>
> I declare an object of type CRowsetGuidUrlServabillityConnectionType
(which
> is of type CCommand) and then use the open() method on that object,
passing
> in the dastabase session and the query string. That performs the query and
> fills the RowsetGuidUrlServabillityConnectionType object with the results.
I
> can then use that object's MoveFIrst() and MoveNext() to retrieve the
> individual records.
>
> CRowsetGuidUrlServabillityConnectionType
> RowsetGuidUrlServabillityConnectionType;
> HRESULT hResult;
> //TRACE("DB -->RowsetGuidUrlServabillityConnectionType.Open\n");
> hResult = RowsetGuidUrlServabillityConnectionType.Open(m_Session,
strQuery);
>
>
> I guess I need someone who understands the CAccessor and CCommand
templates.
>
>
Author
12 Jan 2006 11:43 PM
Roger Garrett
Jim,

You're right, in that the problem is in my code. Well sort of my code. I
took some code from a Microsoft example on setting up a CCommand/CAccessor to
retrieve the recordset. In that sample code it adds properties, as follows:

CDBPropSet propset(DBPROPSET_ROWSET);
propset.AddProperty(DBPROP_IRowsetChange, true);
propset.AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE |
    DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE);

But all I'm trying to do is read some data, not edit, add, or delete the
recordset's records, so those DBPROPVAL_ properties are not needed. In fact,
they appear to be the source of the lengthy processing times when doing the
MoveFirst and MoveNexts. When I remove those DBPROPVAL_s that provide for the
change, insert, and delete, it goes nice and fast.

Thank you to each of you who offerred advice on this issue.

- Roger Garrett

AddThis Social Bookmark Button