Question

VBA Access: how to optimize this routine

Asked by: andy7789

Hi X-perts,

The table has three fields: ID, Date and Returns. I need to loop through all distinct IDs (financial instruments) and do something with each instrument Returns.

The attached code takes too long as it opens a closes over 10,000 recordsets in a loop. How can I optimize it?

I am thinking to dump the whole table to a variant (very fast), but cannot figure out how to extract returns for separate instruments.

I can obviously loop through IDs (to check if they are the same), but, probably, it is not the best way.

Any suggestions for such an algorithm?

Thanks

Set Recordset = New ADODB.Recordset
SQL = "SELECT DISTINCT [FUND ID] from Returns"
Call Recordset.Open(SQL, conHFI, CursorTypeEnum.adOpenForwardOnly, LockTypeEnum.adLockreadOnly, CommandTypeEnum.adCmdText)
fundIDs = Recordset.GetRows
FundsNo = UBound(fundIDs, 2)
 
Recordset.Close
Set Recordset = Nothing
For i = 0 To FundsNo
 
    Set Recordset = New ADODB.Recordset
    SQL = "SELECT [Fund ID],[Date],[Return] FROM Returns WHERE Returns.[Date] >= #" & firstdate & "# AND [FUND ID] = " & fundIDs(0, i)
    Call Recordset.Open(SQL, conHFI, CursorTypeEnum.adOpenForwardOnly, LockTypeEnum.adLockreadOnly, CommandTypeEnum.adCmdText)
    temp = Recordset.GetRows
    Recordset.Close
    Set Recordset = Nothing
    'do something with temp
Next

                                  
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:

Select allOpen in new window

This Question has been solved and asker verified All Experts Exchange premium technology solutions are available to subscription members.

Subscribe now for full access to Experts Exchange and get

Instant Access to this Solution

  • Plus...
  • 30 Day FREE access, no risk, no obligation
  • Collaborate with the world's top tech experts
  • Unlimited access to our exclusive solution database
  • Never be left without tech help again

Subscribe Now

Asked On
2009-06-23 at 02:28:54ID24514007
Topics

Microsoft Access Database

,

VB Objects

,

VB Script

Participating Experts
2
Points
500
Comments
13

Trusted by hundreds of thousands everyday for fast, accurate and reliable tech support.

  • "The time we save is the biggest benefit of Experts Exchange to Warner Bros. What could take multiple guys 2 hours or more each to find is accessed in around 15 minutes on Experts Exchange." Mike Kapnisakis, Warner Bros.
  • "Our team likes having a resource that is more secure than just using Google and most experts using this service really know their stuff. It's nice to look here first versus using Google." Dayna Sellner, Lockheed Martin
  • "Anytime that I've been stumped with a problem, 9 out of 10 times Experts Exchange has either the accepted solution or an open discussion of the potential solution to the problem." Kenny Red, eBay Inc.

See what Experts Exchange can do for you.

Got a question?

We've got the answer.

Experts Exchange has been collecting answers to technology questions since 1996…3 million and counting! If you have a question, chances are we already have your answer.

Screenshot of Experts Exchange Knowledgebase

Need individual assistance?

Our experts are ready to help.

If you can't find the exact answer you're looking for, ask our exclusive community of 50,000 experts. You’ll get a personalized answer from a trusted professional.

Screenshot of Experts Exchange Knowledgebase

Want to learn from the best?

Read articles from industry experts.

Thousands of free tech tips, tricks, how-to’s and tutorials are available in our peer reviewed articles section. See for yourself how smart our experts are, no login required.

Screenshot of an Article

Working on a long term project?

Store your work and research.

Save solutions to your questions, answers you’ve discovered through searching plus helpful articles in your personal knowledgebase for easy future access.

Screenshot of Experts Exchange Knowledgebase

Access the answers to your technology questions today.

Subscribe Now

30-day free trial. Register in 60 seconds.

What Makes Experts Exchange Unique?

Members of the expert community talk about why the experience at Experts Exchange is different than what you will find anywhere else.

Trusted by the world's most respected brands.

image of each brand's logo

Faithfully serving IT professionals since 1996.

Experts Exchange Logo

Try it out and discover for yourself.

Subscribe Now

30-day free trial. Register in 60 seconds.

Related Solutions

  1. Optimization of scaling algorithm
    Hello all, I have the following code to scale a given image by the amount of scaling factor given as argument. But this algorithm takes time to scale the given image. I want that to be done instaneously like in MS-Paint. The code is here: void OrdinaryScaleXY(float xs, flo...
  2. distinct & recordset
    1. I have a selft populating dropdown box which gets its data from a database. It is repeating the same entries, how do I make these distinct so they only display each data entry once please? 2. How do I create a recordset so that the asp page will display the the relevant d...
  3. Algorithm right?
    I need to write for the program that computes A th smallest number in a set of K distinct integer. But there's an restriction that the running time has to be O(K+Alog(K)). This is my way of thinking. I think I will need to sort the data first. And I'm think to use basket...
  4. Algorithms
    I'm looking for two algorithms to work under VBA 1. A Set Covering algorithm, 2. Max. Independent Set or Max Clique algorithm 500 points for each working algorithm Thanks,
  5. Finance Charge Computation
    I am trying to create a statement to send to customers. I would like the statement to include a finance charge. The underlying recordset of my report returns a row for each outstanding invoice. The finance charge computation is like this for each line: BalanceDue * (Inv...

Free Tech Articles

  1. WARNING: 5 Reasons why you should NEVER fix a computer for free.
    It is in our nature to love the puzzle. We are obsessed. The lot of us. We love puzzles. We love the challenge. We thrive on finding the answer. We hate disarray. It bothers us deep in our soul. W...
  2. SCCM OSD Basic troubleshooting
    SCCM 2007 OSD is a fantastic way to deploy operating systems, however, like most things SCCM issues can sometimes be difficult to resolve due to the sheer volume of logs to sift through and the dispe...
  3. Migrate Small Business Server 2003 to Exchange 2010 and Windows 2008 R2
    This guide is intended to provide step by step instructions on how to migrate from Small Business Server 2003 to Windows 2008 R2 with Exchange 2010. For this migration to work you will need the fo...
  4. Create a Win7 Gadget
    This article shows you how to create a simple "Gadget" -- a sort of mini-application supported by Windows 7 and Vista. Gadgets can be dropped anywhere on the desktop to provide instant information, ...
  5. Outlook continually prompting for username and password
    There have been a lot of questions recently regarding Outlook prompting for a username and password whilst using Exchange 2007. There are a few reasons why this would happen and I will try to cover t...
  6. Backup Exchange 2010 Information Store using Windows Backup
    There seems to be quite a lot of confusion around the ability to backup Exchange 2010 using the built in Windows Backup feature. This stems from the omission of this feature prior to Exchange 2007 s...

Cloud Class Webinars

  1. Avoiding Bugs in Microsoft Access
    Alison Balter takes and in-depth look at avoiding bugs in Access. In this webinar you will learn about using the immediate window to debug your applications, invoking the debugger, using breakpoints to troubleshoot, stepping through code, setting the next statement to execute, ...
  2. Top 10 Best New Features in Visio 2010
    Scott Helmers gives live demonstrations of the top 10 new features in Visio 2010. This webinar will teach you how to create compelling diagrams by adding shapes to the page with a single click, linking the shapes in a diagram to data in Excel (or SQL Server, or SharePoint), ...
  3. IT Consultant Business Secrets Revealed
    Michael Munger, Experts Exchange tech pro and IT consultant, pulls back the curtain on his very successful businesses and answers question on every IT consultant and business owner should know about. He shares secrets on what he did to solve the 5 most common problems in IT, ...
  4. Disaster Recovery and Business Continuity
    Quest CTO, Mike Billon, gives an overview of the steps involved in building a dunamic disaster recovery plan. Through case studies and an examination of software/hardware tooles for monitoring and testing, you'll gain a better understandin of where you are, where you want ...
  5. Organize Your Visio Diagrams with Containers and Lists
    Scott Helmers uses cross functional flowcharts, wireframe diagrams, data graphic legends and seating charts to teach you: how to ustilize all three new structured diagram components in Visio 2010, the best practices for organizeing shapes in previous version of Visio, how to organize ...
  6. How to Us Objects, Properties, Events and Methods in Microsoft Access
    Alison Dalter gives an in-depbth look at objects, properties, events and methods in Microsoft Access. In this webinar you will learn about using the object browser, referring to objects, working with properties and methods, working with object variables, understanding the ...

Join the Community

Give a Little. Get a Lot.

Join the community of experts here and help other tech pros by answering question in your area of expertise. You can earn FREE access to all Experts Exchange's premium features and resources.

Join the Community

Answers

 

by: boag2000Posted on 2009-06-23 at 03:34:59ID: 24690525

<I need to loop through all distinct IDs >
You have a "Instruments" table I presume?

Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("SELECT Fld1, Fld2, Fld3, FROM tblInstruments")
 rs.MoveFirst
    Do Until rs.EOF
        'Do something
        rs.MoveNext
    Loop

    MsgBox "Done."

'Cleanup
rs.Close
Set rs = Nothing

JeffCoachman

 

by: andy7789Posted on 2009-06-23 at 04:06:44ID: 24690646

No it doesn't work that way, because I don't know the IDs in advance. I have to loop through all the IDs existing in the table.

It doesn't look I can do it via a single query. The best what I have found so far is to dump the whole recordset to a variant and next work with the variant as an array. I can save time on opening/closing recordsets (only one opening)

 

by: boag2000Posted on 2009-06-23 at 04:45:19ID: 24690847

<No it doesn't work that way, because I don't know the IDs in advance>
Your question was:
"I need to loop through all distinct IDs (financial instruments) and do something with each instrument Returns. "

This is what my code does.
It simply loops through all the records in the table, so there is no need to know the ID's at all.

 

by: LPurvisPosted on 2009-06-23 at 04:54:27ID: 24690901

Just to mention a couple of things...

Recordset isn't a good choice for a variable name. It's already (potentially) 2 object types in your referenced libraries.
Date is a potentially problematic field name too (you're having to maintain backets around it).

Vitally, what is the "something" you intend to do with temp?
Pass it to a procedure of some sort? This procedure expects an array as a parameter?

I'm just wondering why, having opened a recordset (with at least 10,000 rows in the source table) you're wanting to then dump all that into an array (the same data, briefly held twice in memory) to work on further.
Sticking with a single recordset throughout could be perfectly reasonably performing.

Are you still wanting to do "something" with temp - even if it's empty?
If not then the original recordset that you open should surely be filtered upon [Date] >= #" & firstdate & "#" also yes?
Making your resultset smaller, your memory footprint smaller, performance better and less to do.
When opening such a recordset, you'll want to explicitly sort the results into ID order - so you can perform that checking when the ID changes. (You can't reply on the table ordering).

SQL = "SELECT [Fund ID], [Date], [Return] FROM Returns WHERE [Date] >= #" & firstdate & "# ORDER BY [FUND ID] "

And iterate through that... depending upon what "something" is.

 

by: andy7789Posted on 2009-06-23 at 05:46:46ID: 24691249

Thank you!

boag2000, please, have a look at my initial code:

1) The table contains a number of rows with the same Instrument ID (different return and date fields

2) my final task is to get a set of returns (dates) corresponding each unique Instrument ID

the table looks as

ID                                          Date                                        Returns
745                                      05/01/2005                             0.78
745                                      06/01/2005                             1.24
745                                      07/01/2005                             0.66
745                                      08/01/2005                             1.22
978                                      11/01/2003                             0.18
978                                      12/01/2003                             1.62

The reason why i am dumping the whole recordset to an array is because that way I don't open/close 1000s recordsets. it is much faster to manipulate with arrays instead.

I am attaching the code. Will I gain something if I loop through a recordset instead of temp()?

    Set Recordset = New ADODB.Recordset
    SQL = "SELECT [Fund ID],[Date],[Return] FROM Returns WHERE Returns.[Date] >= #" & firstdate & "#"
    Call Recordset.Open(SQL, conHFI, CursorTypeEnum.adOpenForwardOnly, LockTypeEnum.adLockreadOnly, CommandTypeEnum.adCmdText)
    temp = Recordset.GetRows
    totNo = UBound(temp, 2)
    Recordset.Close
    Set Recordset = Nothing
    curr = temp(0, i)
    M = 0
   
    For i = 0 To totNo
        If (curr = temp(0, i)) Then
            ReDim Preserve currRets(0 To 1, 0 To i - M)
            currRets(0, i - M) = temp(1, i) 'dates
            currRets(1, i - M) = temp(2, i) 'returns
            temp1 = currRets(0, 0)
        Else
            'do something with currRets
            If (UBound(currRets, 2) > 10) Then
                ReDim correlArray(0 To UBound(HFRetOnly))
                dDiff = DateDiff("m", HFDates(0), currRets(0, 0))
                For j = dDiff To Application.WorksheetFunction.Min(UBound(HFRetOnly), UBound(currRets, 2))
                    correlArray(j) = Val(currRets(1, j - dDiff))
                Next
                On Error Resume Next
                If Err.Number <> 0 Then GoTo EE
 
                cor = Application.WorksheetFunction.Correl(HFRetOnly, correlArray)
                
                ReDim Preserve allCorrels(0 To 1, 0 To p)
                allCorrels(0, p) = curr  'fund ID
                allCorrels(1, p) = cor   'current correlation for that ID
                p = p + 1
                On Error GoTo 0
            End If
            'end correl build
EE:
            M = i 'resetting the redim counter
            curr = temp(0, i)
            ReDim Preserve currRets(0 To 1, 0 To i - M)
            currRets(0, i - M) = temp(1, i) 'dates
            currRets(1, i - M) = temp(2, i) 'returns
        End If
    Next

                                              
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:

Select allOpen in new window

 

by: andy7789Posted on 2009-06-23 at 06:45:05ID: 24691762

It is the same code looping throughout  the recordset.

The execution time for 10,000 records is 1sec, but for the previous code (dumping a recordset to a variant) is 0sec.

So, cycling through a recordset is much slower

  Set Recordset = New ADODB.Recordset
    SQL = "SELECT [Fund ID],[Date],[Return] FROM Returns WHERE Returns.[Date] >= #" & firstdate & "#"
    Call Recordset.Open(SQL, conHFI, CursorTypeEnum.adOpenForwardOnly, LockTypeEnum.adLockreadOnly, CommandTypeEnum.adCmdText)
    curr = Recordset.Fields("Fund ID").Value
    M = 0
    st = Now
   Do While Not Recordset.EOF
    
        If (curr = Recordset.Fields("Fund ID").Value) Then
            ReDim Preserve currRets(0 To 1, 0 To i - M)
            currRets(0, i - M) = Recordset.Fields("Date").Value 'dates
            currRets(1, i - M) = Recordset.Fields("Return").Value 'returns
            temp1 = currRets(0, 0)
            
        Else
            'do something with currRets
            If (UBound(currRets, 2) > 10) Then
                ReDim correlArray(0 To UBound(HFRetOnly))
                dDiff = DateDiff("m", HFDates(0), currRets(0, 0))
                For j = dDiff To Application.WorksheetFunction.Min(UBound(HFRetOnly), UBound(currRets, 2))
                    correlArray(j) = Val(currRets(1, j - dDiff))
                Next
                On Error Resume Next
                If Err.Number <> 0 Then GoTo EE
 
                cor = Application.WorksheetFunction.Correl(HFRetOnly, correlArray)
                
                ReDim Preserve allCorrels(0 To 1, 0 To p)
                allCorrels(0, p) = curr  'fund ID
                allCorrels(1, p) = cor   'current correlation for that ID
                p = p + 1
                On Error GoTo 0
            End If
            'end correl build
EE:
            M = i 'resetting the redim counter
            curr = Recordset.Fields("Fund ID").Value
            ReDim Preserve currRets(0 To 1, 0 To i - M)
            currRets(0, i - M) = Recordset.Fields("Date").Value  'dates
            currRets(1, i - M) = Recordset.Fields("Return").Value 'returns
        End If
    Recordset.MoveNext
    i = i + 1
    Loop
Debug.Print DateDiff("s", st, Now()) & "s"

                                              
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:

Select allOpen in new window

 

by: boag2000Posted on 2009-06-23 at 06:59:49ID: 24691925

I will yield to LPurvis,

Perhaps he is understanding something that I am not.

Again, in the most basic sense, this sounds easy:

Dim rs As DAO.Recordset
Dim strDates
Set rs = CurrentDb.OpenRecordset("SELECT ID, [Date], Returns, FROM Yourtable")
 rs.MoveFirst
    Do Until rs.EOF
        strDates=strDates & "," & rs![Date]
        rs.MoveNext
    Loop

    MsgBox "Done." & vbcrlf & strDates
    me.txtDates= strDates

'Cleanup
rs.Close
Set rs = Nothing


This loops all the records and "accumulates" all the dates.
You can Accumulate them with a Carrige return and a line feed if you like (vbcrlf), and do with this list what you please.

Again, if I am not understanding something, I am sure LPurvis can help you from here on out.

But can you post a sample of what the output would be based on the sample data you posted?

JeffCoachman

 

by: LPurvisPosted on 2009-06-23 at 09:02:07ID: 24693191

So it is indeed another function which is expecting an array passed to it - fair enough.
(I see this is Excel based code - not a big issue in itself, but using ADO as you are is generally easier than DAO - but there's no reason why you couldn't add a DAO reference and use it instead - it might be that little faster using Jet data as you apparently are).

Your earlier code (opening repeated recordsets) is clearly going to have incurred massive overhead.
Especially depending upon the contents (and hence type) of your conHFI variable.
If it's a connection string then whoa - yep, slow. If it's an actual connection object that's persisted for the duration then fair enough. (If it were a function returning a new connection then see 1 again ;-)
In the latter example that doesn't matter so much - but is still worth us knowing what it is defined as.
(For example a Data Shaping connection provider wouldn't be appropriate IMO).

There are things to try to speed up the recordset code if you were so inclined.
For example different cursor types. You're using a ForwardOnly which, in theory, is the most efficient.
Although a static would mean a much heavier initial load, subsequent fetches would be fast.
And little advantages, such as declaring explicit field variables and using those to reference the recordset values.
For example

Dim fld1 As ADODB.Field
Dim fld2 As ADODB.Field
'...

'After opening the recordset...
Set fld1 = Recordset.Fields("Date")
Set fld2 = Recordset.Fields("Return")

'...and then...
currRets(0, i - M) = fld1.Value
currRets(1, i - M) = fld2.Value

That won't make the difference from 1 seconds to zero, I can't think what would.
But then - I'd expect a vast majority of the overhead in the array method to be the acquisition of the array using GetRows.
Perhaps you have a more memory replete machine than I (wouldn't be hard).
Still... what were your exact timings I wonder... (100,000 rows in zero seconds? :-s)

 

by: andy7789Posted on 2009-06-23 at 15:35:23ID: 24696983

Thank you. a few more details:

1) Yes, conHFI is a string, not an object (see attached)

2) I am using Jet... probably, should try DAO

3) the exact execution times:

a) code I (looping throughout a dataset): 0.765 sec
b) code II (looping throughout an array): 0.312 sec
Total rows: 69070
PC: Dell Core Duo 2.5GHz, 8Gb RAM

In answer to Jeff's question:
<But can you post a sample of what the output would be based on the sample data you posted?>

1) I need to calculate correlations (one-by-one) of the extracted returns for all instruments with a given return string

2) The output should be something like a matrix

745 (Ret)                  745(Dates)                        978(Ret)             978(Dates)                       etc(another ID)
--------------------------------------------------------------------------------------------------------------------------------------
0.78                             12/1/2005                        0.92                       03/4/2006                     etc
0.23                             01/1/2006                        0.54                        04/4/2006

as you can see, all series have different lengths, i.e. a max number of rows should be counted on the longest one

I am thinking even of building a temp table with that structure once to improve performance on subsequent calls later. On the other hand, 0.3 sec is not that bad and I could live with it

Public Const conHFI As String = _
"Provider=Microsoft.Jet.OLEDB.4.0;" + _
"Data Source=C:\HFData\HFI\download.mdb;" + _
"Persist Security Info=False"

                                              
1:
2:
3:
4:

Select allOpen in new window

 

by: LPurvisPosted on 2009-06-23 at 15:59:36ID: 24697129

1) Establishing a connection each time meant your recordset opening method was nailing resources.

2) Absolutely worth trying. No harm in that. As mentioned, it should be that bit quicker with Jet data. I'd recommend also using the Field objects suggestion I made earlier to get that extra bit of performance.

3) 0.765 sec Vs 0.312 sec...
Well... That's hardly 1 Vs 0 is it? :-p
Certainly one's twice the time of the other... But that's not insurmountable.

8Gb RAM??
So what OS then? Vista 64?
What version of Office?  (Just curious really).
The large memory would possibly explain the relative GetRows efficiency compared to subsequent processing.

 

by: andy7789Posted on 2009-06-24 at 03:48:15ID: 24699800

No not Vista, obviously. XP
Office 2007
In fact, this particular machine is a notebook :)

I am closing the question. Thank you for your help!

 

by: andy7789Posted on 2009-06-24 at 03:50:19ID: 24699813

XP 32

 

by: LPurvisPosted on 2009-06-24 at 04:13:37ID: 24699944

Well, hopefully something will have helped. (I'd imagine the gap would close - not sure if it would collapse to nothing though).

I personally prefer the functionality of recordsets (I'm a database developer after all ;-) - but in a situation where you're expecting arrays anyway then there's no harm and if it performs better than a clear advantage.


Just as an aside... 8GB in a 32bit edition of XP? Surely over 4 GB of that isn't addressable?
Still - that's for others to worry about. Like I said... database developer. lol. ;-)

Cheers.

20120131-EE-VQP-002

3 Ways to Join

30-Day Free Trial

The Experts

98% positive feedback on 31,087 answers since March 2000. angeliii is a Microsoft Most Valuable Professional for his work with MS SQL Server & Develoment.

He has also proven his knowledge of Visual Basic Programming, PHP Scripting and Oracle Databases.

The Experts

97% positive feedback on 10,752 answers since July 2000. lrmoore has more than 18 years experience in the networking industry.

The six-time Mircosoft MVPs specialties include firewalls, virtual private networking, and network management.

Testimonials

"...and excellent source for support... Kind of like having your very own IT dept." Electriciansnet

Testimonials

"I was apprehensive at signing up at first. However... it has already made my life as an IT administrator much easier." JaCrews

Testimonials

"WOW! You guys have great, active, and knowledgeable people on here." moore50

Business Clients

Business Clients

In the Press

"If you’ve got a question... Experts Exchange can supply an answer.”

In the Press

"...an invaluable aid for both IT professionals and those who require tech support."

In the Press

"where IT professionals provide quick answers on just about any topic"

Business Account Plans

Loading Advertisement...