Access Sub-Total Function Code 4 u (1 Viewer)

Status
Not open for further replies.

rich.barry

Registered User.
Local time
Today, 07:31
Joined
Aug 19, 2001
Messages
176
A while back I wrote a procedure to carry out sub totals within Access similar to the function available in Excel.
I might as well share it here, and others can hack it around to fit their own needs.

Here's an example of the sort of thing I'm using it for.

A production system writes time stamped machinery setting data to a table at regular intervals. I then want a list of start and stop time of the production runs, a run being defined as each time they change product thickness.

First I'm going to make a query (qryPressRuns) to get the relevant data from the table
SELECT MW_Vis_Data.VisDateTime AS Time1, MW_Vis_Data.VisDateTime AS Time2, MW_Vis_Data.Thickness FROM MW_Vis_Data;

in this case I've extracted the time stamp twice in the query, as I want a start time and a stop time for the runs

Next I call my sub total function with the following code

Sub test()
SubTotals "qryPressRuns", "TableOut", sbQuery, "Thickness", "Time1/Min,Time2/Max,Thickness/Min", sbDisplaySubTotals
End Sub

Arguments of the sub total function are:

Data source table/query/SQL statement
Data source type = sbTable, sbQuery or sbSQL
Output table name
Field name which change in is going to trigger sub total
List of Field names being subtotaled and type of sub total e.g. "Time1/Min,Time2/Max"
sbDisplaySubTotals or sbDisplayAll depending on whether you want the data as well as the sub total

At present, Max,Min,Count,Sum and Average are supported in the code, but you can program any others you want to.
In the output table, an IndexNumber and SubTotal fields are generated. Sorting by the IndexNumber when you are displaying data as well as sub totals will put the sub total at the bottom of each data segment, whilst sorting by the subtotal will list all the data, then all the sub totals

Function code is below.

Hope this helps someone.

Code:
Function SubTotals(sbDataSource As String, sbSourceType As Integer, sbOutputTable As String, sbChangeIn As String, sbFieldData As String, sbDisplay As Integer) As String
'Syntax example
'SubTotals "qrylogsuppliersummary", "TableOut", sbQuery, "supplierid", "Logcount/Sum,DocketNumber/Count,NonConformances/Sum,avgofHitVelocity/Average", sbDisplayAll

Dim SourceData As New ADODB.Recordset
Dim Result As New ADODB.Recordset
Dim strSQL As String
Dim strRunSQL As String
Dim ChangeInCurrent As String
Dim commapos As Integer
Dim fieldx(100, 2) As String    '1=Field to Sub Total, 2=Type of Sub Total
Dim totals(100) As Double
Dim min(100) As Variant
Dim max(100) As Variant
Dim WriteFlag As Boolean
Dim CountofRecords(100) As Long
Dim pos As Integer
Dim v As Variant
Dim X As Integer
Dim c As Integer
Dim IndexNumber As Long

'Delimit the data in sbFieldData. X is the number of fields being sub totalled
X = 1
'If Right(sbFieldData, 1) <> "," Then sbFieldData = sbFieldData & ","
For pos = 1 To Len(sbFieldData)
    If Mid(sbFieldData, pos, 1) <> "," Then
        fieldx(X, 1) = fieldx(X, 1) & Mid(sbFieldData, pos, 1)
    Else
        If fieldx(X, 1) <> "" Then X = X + 1
    End If
Next pos

For c = 1 To X
    fieldx(c, 2) = Mid(fieldx(c, 1), InStr(1, fieldx(c, 1), "/") + 1)
    fieldx(c, 1) = Left(fieldx(c, 1), InStr(1, fieldx(c, 1), "/") - 1)
Next c

'Open the source data
Select Case sbSourceType
Case 1, 2
    strSQL = "SELECT 999.9 AS IndexNumber, * FROM " & sbDataSource
Case 3
    strSQL = sbDataSource
End Select

'Create the result table. The WHERE FALSE criteria creates the table structure without data.
pos = InStr(1, strSQL, "FROM")
strRunSQL = Left(strSQL, pos - 1) & "INTO " & sbOutputTable & " " & Mid(strSQL, pos)
If sbDisplay = sbDisplaySubTotals Then strRunSQL = strRunSQL & " WHERE False"

DoCmd.SetWarnings False
DoCmd.RunSQL strRunSQL
DoCmd.SetWarnings True

With DBEngine(0)(0).TableDefs(sbOutputTable)
  .Fields.Append .CreateField("SubTotals", dbText, 100)
End With

'SubTotal the data
SourceData.Open strSQL, CurrentProject.Connection, adOpenKeyset, adLockPessimistic
Result.Open "SELECT * FROM " & sbOutputTable, CurrentProject.Connection, adOpenKeyset, adLockPessimistic
    
IndexNumber = 1

'If displaying all data, then add an index number for subsequent sorting
If sbDisplay = sbDisplayAll Then
    With Result
        .MoveFirst
        Do While Not .EOF
            !IndexNumber = IndexNumber
            .Update
            .MoveNext
            IndexNumber = IndexNumber + 1
        Loop
    End With
End If

With SourceData
    .MoveFirst
    IndexNumber = 0
    Do
        If .AbsolutePosition <> adPosEOF Then
            'Read initial value of field being monitored for change
            If .AbsolutePosition = 1 Then ChangeInCurrent = Nz(.Fields(sbChangeIn), 0)
            If .Fields(sbChangeIn) <> ChangeInCurrent Then
                WriteFlag = True
            End If
        Else
            WriteFlag = True
        End If
        
        If WriteFlag = True Then
            'write new subtotal
            Result.AddNew
            Result.Fields(sbChangeIn) = ChangeInCurrent
            Result.Fields("SubTotals") = ChangeInCurrent & " Sub-Total"
            Result!IndexNumber = IndexNumber + 0.1
            If .AbsolutePosition <> adPosEOF Then ChangeInCurrent = .Fields(sbChangeIn)
            
            For c = 1 To X
                Select Case fieldx(c, 2)
                Case "Sum"
                        v = totals(c)
                Case "Average"
                        If CountofRecords(c) > 0 Then
                            v = totals(c) / CountofRecords(c)
                        Else
                            v = 0
                        End If
                Case "Count"
                        v = CountofRecords(c)
                Case "Max"
                        v = max(c)
                Case "Min"
                        v = min(c)
                End Select
                Result.Fields(fieldx(c, 1)) = v
            Next c
            
            Result.Update
            Erase totals()
            Erase CountofRecords
            Erase min()
            Erase max()
            WriteFlag = False
        End If
        
        If .AbsolutePosition = adPosEOF Then Exit Do
        
        For c = 1 To X
            totals(c) = totals(c) + Val(Nz(.Fields(fieldx(c, 1)), 0))
            'Don't index the record count for a null field for average calculations
            If fieldx(c, 2) <> "Average" Or (fieldx(c, 2) = "Average" And Not IsNull(.Fields(fieldx(c, 1)))) Then CountofRecords(c) = CountofRecords(c) + 1
            If IsNull(max(c)) Or max(c) = "" Or .Fields(fieldx(c, 1)) > max(c) Then max(c) = .Fields(fieldx(c, 1))
            If IsNull(min(c)) Or min(c) = "" Or .Fields(fieldx(c, 1)) < min(c) Then min(c) = .Fields(fieldx(c, 1))
        Next c
        IndexNumber = IndexNumber + 1
    .MoveNext
    Loop
    .Close
End With

Set SourceData = Nothing

Result.Close
Set Result = Nothing

SubTotals = sbOutputTable

End Function
 
Status
Not open for further replies.

Users who are viewing this thread

Top Bottom