Help to improve efficiency of VBA

simon03

Registered User.
Local time
Today, 22:50
Joined
Aug 13, 2014
Messages
40
Hi,

I have written a Subroutine in VBA 7 for Access 2010 that implements a recursive formula. In Excel the formula looks something like this:

Code:
=IF(MONTH(B2)=1,100*(1+C2/D2),E1*(1+C2/D2))

Where the formula is applied for cells of column E

The subroutine that I have written in Access works fine but is too slow. My table has about 100,000 records but the subroutine takes about 4-5 minutes even when I process only 500 records.

No need to say that I am a newbie in Visual Basic and programming is not one of my strongest skills. The code is probably not well written and I am hoping that some of you could provide suggestions to improve it!

Thanks

Code:
 Sub Test_Calculation()
    
    Dim curDatabase As Database
    Dim tbl As TableDef
    Dim FieldName As Field
    
    Dim x As Double
    Dim y As Double
    Dim z As Double
    Dim num As Double
    Dim den As Double
    Dim num_mbefore As Double
    Dim den_mbefore As Double
    Dim month_before As String
    Dim cur_asset As String
    Dim CurMonth As Integer
    Dim Year As Integer
    Dim a As Integer
    Dim b As String
    
    
    Dim Rst As DAO.Recordset
    Dim Rst2 As DAO.Recordset
    
    Dim fld As DAO.Field
    Dim tdef As DAO.TableDef
   
    
    ' Get a reference to the current database
    Set curDatabase = CurrentDb
    Set tdef = curDatabase.TableDefs("IPD_asset")
        
    'Create the field CalcTest or clean it if it exists
    a = 0
    For Each fld In tdef.Fields
        If fld.Name = "CalcTest" Then
            CurrentDb.Execute "UPDATE IPD_asset SET CalcTest = Null", dbFailOnError
            a = a + 1
            
        End If
    Next
    If a < 1 Then
       curDatabase.Execute "ALTER TABLE IPD_asset ADD COLUMN CalcTest Double"
    End If
        
        
    'Select subset of table between two dates and order it (filter)
    Set Rst = CurrentDb.OpenRecordset("SELECT * FROM IPD_asset Where Month Between #01/01/1990# and #01/01/1991# Order By Month ")
    
    If Not (Rst.EOF And Rst.BOF) Then
        Rst.MoveFirst
        Do Until Rst.EOF = True

            If Month(Rst.Fields("Month").Value) = 1 Then
                num = Rst.Fields("Total Return Num")
                den = Rst.Fields("TR/IR/CG Den")
                If den = 0 Then
                    x = 0
                Else
                    x = num / den
                    x = x + 1
                    x = 100 * x
                End If
                
                Rst.Edit
                Rst.Fields("CalcTest") = x
                Rst.Update
            Else
                'Parses the date to get the previous months date
                Year = DatePart("yyyy", Rst.Fields("Month"))
                CurMonth = Month(Rst.Fields("Month"))
                cur_asset = Rst.Fields("IPD Ref").Value
                
                CurMonth = CurMonth - 1
                
                month_before = "#" + CStr(CurMonth) + "/1/" + CStr(Year) + "#"
                
                'here we select the calculated value for the previous month and the correct IPD ref
                Set Rst2 = CurrentDb.OpenRecordset("SELECT * FROM IPD_asset WHERE Month =" & month_before & " AND [IPD Ref]='" & cur_asset & "'")
                
                If Not (Rst2.EOF And Rst2.BOF) Then
                
                
                    z = Rst2.Fields("CalcTest")
                
                    'Current month Calculations with the Calculted Value from the previous month
                    num = Rst.Fields("Total Return Num")
                    den = Rst.Fields("TR/IR/CG Den")
                    If den = 0 Then
                        y = 0
                    Else
                        y = num / den
                        y = y + 1
                        y = z * y
                    End If
                Else
                    
                    y = num / den
                    y = y + 1
                    y = 100 * y
                End If
                
                Rst.Edit
                Rst.Fields("CalcTest") = y
                Rst.Update
            End If
            Rst.MoveNext
        Loop
    Else
        MsgBox "There are no records in the recordset."
    End If
    
    MsgBox "Finished looping through records."

Cleanup:
    Rst.Close
    Set Rst = Nothing
    Rst2.Close
    Set Rst2 = Nothing
    
End Sub
 
simon03, Welcome to AWF :)

It is great you have indented your code, explained the problem. However the code you gave makes very little sense to a third person (atleast to me). So how about you say it simply. In words explain what you are trying to do, so we can suggest you a better or correct code.
 
No need to say that I am a newbie in Visual Basic and programming is not one of my strongest skills.
We weren't even going to go there ;)

Code:
=IF(MONTH(B2)=1,100*(1+C2/D2),E1*(1+C2/D2))

My table has about 100,000 records but the subroutine takes about 4-5 minutes even when I process only 500 records.
Like pr2-eugin said it's hard to understand your code without any comments but from the above I can see what you're trying to do. I just hope you're not thinking of Access like Excel.

In plain english:
1. If the Month of the date field (B) is January, then
a. 1 + field C/field D multiplied by 100
b Otherwise multiply it by field E but you've put E1 which would mean the previous record?

You can do this all in a query without needing to save the calculated value or even needing a recordset.
 
Hi,

Thanks for your answer. I will try to make it as clear as possible :)

I have a table in my DB, this table has the following fields:

ID, name, date, num, den

What I want to do is to create a new field which has the following value:

Code:
 If the month is January 

= 100 * (1+num/den)

Else

take the value of the previous month and multiply it times (1+num/den) of the current month

Thus the recursive formula which applies for months between Feb and Dec because I need the value of the previous month to calculate the value of the current month.

I hope this clarifies what I want to do. As far as I have understood this formula cannot be developed in SQL within Access.

Thanks,
S.
 
As far as I have understood this formula cannot be developed in SQL within Access.
Can be, but should not be. Calculated fields should not sit in a table. Excel and Access are two different applications. They do not work on a LIKE by LIKE basis. So the idea of calculated fields should be dropped. More info on Calculated Fields : http://allenbrowne.com/casu-14.html

Although this does not mean that you cannot do it, you can create unbound controls on a Form to get the desired result, or use a Query (as vbaInet has mentioned) to get the information required. It would take a matter of seconds to get this sorted.
 
OK, I understand the issue. I am thinking in the wrong way. No need to store my calculated values just use a query and do whatever I want with it.

I will post my new code as soon as I modify it (just for the sake of completeness of this thread)

Thanks
 
What we're also saying is that you probably don't need code for this. You can probably do it all in a query but we need more information about your tables.
 
The table has 5 fields as follows:

ID: Primary Key
Name: String
Date: Date in the format dd/mm/yyyy
Num: Double
Den: Double

It might happen that Num and Den are 0 but not null.

Is there a way around SQL in Access to implement this? Do you need more information?
 
The field names, Name and Date, are they ones you created or it came from the source? The reason I ask is those names are reserved for Access/VBA and although they can be used, it just means you would have to always enclose them in square brackets when used in queries or called in code.
Here's a comprehensive list of reserved keywords:
http://allenbrowne.com/AppIssueBadWord.html

Yes. Have you used queries before or at least written SQL?
 
I am back but I am still not sure that a query would solve my issue (in VBA or just SQL). I am probably missing the concept behind queries ..

I am wondering how I can call a calculated value in order to calculate another calculated value using a query (i.e. to implement what I have described before).

My point is if the query displays values that already exist in the DB or values that are directly calculated from existing fields, now in my case how can a Query calculate a value if it needs another calculated value that is calculated in the same process?

Sorry if it is a stupid question but also searching online I have the impression that Access does not allow to do this (or only if I store my calculated values somewhere and I recall them).

EDIT: Thanks for the update. The fields are actually called with different names. I just reported them in this way for clarity

Thanks,
S.
 
Last edited:
Num and Den don't communicate much to anyone but the OP. Can you tell us what these represent?
 
They are numbers and represent performance values of an element in time (monthly basis). I need the ratio between them but each month performance is evaluated based on the previous month performance (it's a cumulative performance) and the base month is January or the first month of record of each year.
 
I am back but I am still not sure that a query would solve my issue (in VBA or just SQL). I am probably missing the concept behind queries ..

I am wondering how I can call a calculated value in order to calculate another calculated value using a query (i.e. to implement what I have described before).

My point is if the query displays values that already exist in the DB or values that are directly calculated from existing fields, now in my case how can a Query calculate a value if it needs another calculated value that is calculated in the same process?

Sorry if it is a stupid question but also searching online I have the impression that Access does not allow to do this (or only if I store my calculated values somewhere and I recall them).

EDIT: Thanks for the update. The fields are actually called with different names. I just reported them in this way for clarity

Thanks,
S.
You should be asking us "how" rather than question whether what we're suggesting is possible or not in Access.

Let's see some sample records and from that show us what you would like achieved. An Excel file with the test (non-confidential) data would suffice.
 
What we're also saying is that you probably don't need code for this. You can probably do it all in a query but we need more information about your tables.

Another vote for doing this in a query and not writing the calculated values back to a table.
 
Hi,

Thanks for the effort you all are making in helping me.

You're right. I did not formulate well my request in my previous post. I meant to ask how to do it because I can't figure it out (even conceptually !! :banghead:).

I have attached an Excel file data sample which includes two elements (a and b). a's values range between Jan-11 and Dec-12; b's values between Jan-11 and Dec-11.

My recursive formula is included in Column F ('Calc_Field').

Many thanks,
S.
 

Attachments

Now that we can see the data set it makes more sense. I didn't know what E1 represented but it's clear now. I'll look into it later and post back.
 
One more thing, is the date always going to be sorted in alphabetical order from the source? Or is this a sort you applied?
And you say that there will be around 100k records?
 
I sorted the provided sample by date for you but if that could help in solving the problem I can sort the records by date before importing them in my Access DB.

At the moment I have about 120,000 records but they will grow in number in future.

Thanks again!

S.
 
I don't mind how it comes, I just need to know how it actually comes from the source? Sorted or not sorted?

And lastly, is that really how the date comes through as well? It's not a proper date, just month & year.
 
Answers:

1) it comes not sorted

2) the date is just formatted in that way in the Excel file but if you check the row data (click inside each cell) the format is dd/mm/yyyy. Sorry I should have removed the format from the cells.
 

Users who are viewing this thread

Back
Top Bottom