Solved

# Aggregating fields

Posted on 2011-05-09
241 Views
Guys/Gals,

I have more than one record in a table, but for the purpose of this question I have 3 records by way of example.

Record ID      Value 1      Value 2      Value 3
Record 1            N/A      9430572.14
Record 2      N/A      100      0
Record 3      9406396.79      N/A      8528578.56

All value fields have been set to "text" and each value can be either NULL, "N/A", Number (but looks like text)

What I need to do is aggregate all of these together to form one value (there are actually 8 fields to aggregate). I can simply use Cdbl as it doesn't work for N/A or NULL, I can't use Sum....herein lies my problem.

I am wondering if there is an easy way to do this without using lots of Iif's, or do I really need to write a UDF, if I need a UDF, what would be quick?
0
Question by:Runrigger

LVL 92

Accepted Solution

Try this:

1) If you are aggregating by row...

``````SELECT [Record ID], IIf(IsNumeric([Value 1]), Val([Value 1], 0) AS + IIf(IsNumeric([Value 2]), Val([Value 2], 0) + ... + IIf(IsNumeric([Value 8]), Val([Value 8], 0) AS SummedValues
FROM [SomeTable]
``````

2) If by column...

``````SELECT Sum(IIf(IsNumeric([Value 1]), Val([Value 1], 0)) AS Sum1, Sum(IIf(IsNumeric([Value 2]), Val([Value 2], 0)) AS Sum2, ... Sum(IIf(IsNumeric([Value 8]), Val([Value 8], 0)) AS Sum8
FROM [SomeTable]
``````

BTW, if you are aggregating by row and you really want to do this in code, you can use the RowStats UDF I describe in my article http://www.experts-exchange.com/Microsoft/Development/MS_Access/A_1775-Computing-row-wise-aggregations-in-Access.html

The source code is:

``````Function RowStats(Stat As String, ParamArray Vars())

' Function by Patrick Matthews

' This code may be used and distributed freely, so long as you attribute authorship, and indicate
' what URL you found the code at

' This Access UDF calculates various stats for the values passed into the ParamArray.  It
' was originally designed for passing several values from a particular row set for
' evaluation; since the values come from the same row, the usual aggregate functions would
' not be appropriate.

' The Stat argument (NOT case sensitive) determines what statistic is calculated:
' "count":          count of non-null values
' "min", "max":     minimum or maximum
' "sum", "avg":     sum or average, excluding strings
' "var", "stdev":   sample variance or standard deviation (excluding strings)
' "varp", "stdevp": population variance or standard deviation (excluding strings)

' You may pass any value in the ParamArray.  Strings, nulls, and dates are ignored for the
' sum, avg, stdev, stdevp, var, and varp calculations.  (Thus, you could use this function to
' add several columns together without wrapping each column with Nz() to handle nulls...)

' If run from VBA, this function appears to accept any number of values in the Vars argument;
' I successfully tested passing several hundred values.  If run from the Access query editor,
' the limit appears to be about 28 values.  If you need to process more than 28 values, you
' should use the related function RowStatsFieldList.  (You can overcome this limit by embedding
' RowStats expressions, but this will produce potentially erroneous results for the Avg,
' StDev[P], or Var[P] stats

' Please note that you can pass arrays as elements of the Vars ParamArray; indeed, the
' RowStatsFieldList function does that

' As with the regular aggregate functions, if all of the parameters are null, then the
' return value is null, except on count, where the return would be zero

Dim Numerator As Double
Dim Denominator As Double
Dim Counter As Long, Counter2 As Long
Dim Result As Variant
Dim Mean As Double

' Force to upper case to make sure string comparisons are always performed as expected;
' Access uses Option Compare Database by default, but other VBA/VB6 uses binary default

Stat = UCase(Stat)

Select Case Stat

' In each Case below, loop through the elements of the Vars ParamArray.  If the element
' is itself an array, then loop through its elements

Case "COUNT"

' Increment the result for each non-null value in the array

Result = CLng(0)

For Counter = LBound(Vars) To UBound(Vars)
If Not IsArray(Vars(Counter)) Then
If Not IsNull(Vars(Counter)) Then Result = Result + 1
Else
For Counter2 = LBound(Vars(Counter)) To UBound(Vars(Counter))
If Not IsNull(Vars(Counter)(Counter2)) Then Result = Result + 1
Next
End If
Next

Case "MIN"

' Initialize the result to Null, then check all non-Null values in turn to see
' if it is less.

Result = Null

For Counter = LBound(Vars) To UBound(Vars)
If Not IsArray(Vars(Counter)) Then
If IsNull(Result) And Not IsNull(Vars(Counter)) Then
Result = Vars(Counter)
ElseIf Vars(Counter) < Result Then
Result = Vars(Counter)
End If
Else
For Counter2 = LBound(Vars(Counter)) To UBound(Vars(Counter))
If IsNull(Result) And Not IsNull(Vars(Counter)(Counter2)) Then
Result = Vars(Counter)(Counter2)
ElseIf Vars(Counter)(Counter2) < Result Then
Result = Vars(Counter)(Counter2)
End If
Next
End If
Next

Case "MAX"

' Initialize the result to Null, then check all non-Null values in turn to see
' if it is greater.

Result = Null

For Counter = LBound(Vars) To UBound(Vars)
If Not IsArray(Vars(Counter)) Then
If IsNull(Result) And Not IsNull(Vars(Counter)) Then
Result = Vars(Counter)
ElseIf Vars(Counter) > Result Then
Result = Vars(Counter)
End If
Else
For Counter2 = LBound(Vars(Counter)) To UBound(Vars(Counter))
If IsNull(Result) And Not IsNull(Vars(Counter)(Counter2)) Then
Result = Vars(Counter)(Counter2)
ElseIf Vars(Counter)(Counter2) > Result Then
Result = Vars(Counter)(Counter2)
End If
Next
End If
Next

Case "AVG", "SUM"

' Check each value in turn.  If it is numeric, then increment numerator and denominator.
' Divide numerator by denominator to get an average, or by 1 to get the sum.  Any Date
' values are coerced into Double

For Counter = LBound(Vars) To UBound(Vars)
If Not IsArray(Vars(Counter)) Then
If IsNumeric(Vars(Counter)) Then
Numerator = Numerator + Vars(Counter)
Denominator = Denominator + 1
ElseIf IsDate(Vars(Counter)) Then
Numerator = Numerator + CDbl(Vars(Counter))
Denominator = Denominator + 1
End If
Else
For Counter2 = LBound(Vars(Counter)) To UBound(Vars(Counter))
If IsNumeric(Vars(Counter)(Counter2)) Then
Numerator = Numerator + Vars(Counter)(Counter2)
Denominator = Denominator + 1
ElseIf IsDate(Vars(Counter)(Counter2)) Then
Numerator = Numerator + CDbl(Vars(Counter)(Counter2))
Denominator = Denominator + 1
End If
Next
End If
Next

If Denominator > 0 Then
Result = Numerator / IIf(Stat = "AVG", Denominator, 1)
Else
Result = Null
End If

Case "STDEV", "STDEVP", "VAR", "VARP"

' Take one pass through the set to determine the average, and then determine the
' sum of squared deviances from the mean.  Divide by number of elements in the
' array for population or (elements - 1) for sample.  If standard deviation,
' take square root.  Any Date values are coerced into Double

' This pass generates the numerator and denominator needed for the average

For Counter = LBound(Vars) To UBound(Vars)
If Not IsArray(Vars(Counter)) Then
If IsNumeric(Vars(Counter)) Then
Numerator = Numerator + Vars(Counter)
Denominator = Denominator + 1
ElseIf IsDate(Vars(Counter)) Then
Numerator = Numerator + CDbl(Vars(Counter))
Denominator = Denominator + 1
End If
Else
For Counter2 = LBound(Vars(Counter)) To UBound(Vars(Counter))
If IsNumeric(Vars(Counter)(Counter2)) Or IsDate(Vars(Counter)(Counter2)) Then
Numerator = Numerator + Vars(Counter)(Counter2)
Denominator = Denominator + 1
ElseIf IsDate(Vars(Counter)(Counter2)) Then
Numerator = Numerator + CDbl(Vars(Counter)(Counter2))
Denominator = Denominator + 1
End If
Next
End If
Next

' Make sure there are enough numeric elements to avoid a division by zero error.  If not,
' return Null

If (Stat Like "*P" And Denominator > 0) Or (Not Stat Like "*P" And Denominator > 1) Then

Mean = Numerator / Denominator

' This pass sums the squares of the differences between each data point and the mean

For Counter = LBound(Vars) To UBound(Vars)
If Not IsArray(Vars(0)) Then
If IsNumeric(Vars(Counter)) Then
Result = Result + (Vars(Counter) - Mean) ^ 2
ElseIf IsDate(Vars(Counter)) Then
Result = Result + (CDbl(Vars(Counter)) - Mean) ^ 2
End If
Else
For Counter2 = LBound(Vars(Counter)) To UBound(Vars(Counter))
If IsNumeric(Vars(Counter)(Counter2)) Then
Result = Result + (Vars(Counter)(Counter2) - Mean) ^ 2
ElseIf IsDate(Vars(Counter)(Counter2)) Then
Result = Result + (CDbl(Vars(Counter)(Counter2)) - Mean) ^ 2
End If
Next
End If
Next

' Divide by N for population, and N-1 for sample

If Stat Like "*P" Then
Result = Result / Denominator
Else
Result = Result / (Denominator - 1)
End If

' Take square root if standard deviation

If Stat Like "S*" Then Result = Result ^ 0.5

Else
Result = Null
End If

Case Else

' If Stat is none of the above, then return Null -- invalid Stat

Result = Null
End Select

' Set return value

If Not IsNull(Result) Then RowStats = Result Else RowStats = Null

End Function
``````

You could use it in a query like this:

``````SELECT [Record ID], RowStats("Sum", [Value 1], [Value 2], [Value 3], [Value 4], [Value 5], [Value 6], [Value 7], [Value 8]) AS RowSum
FROM [SomeTable]
``````

When computing a sum, RowStats already "knows" to ignore nulls and text it cannot convert into a number, and it also knows how to convert strings that "look" like numbers into actual numbers.

Patrick
0

LVL 7

Expert Comment

Use ISNumeric function and then do a sum

--Example
SELECT
SUM(CASE WHEN ISNUMERIC(VALUE1) =1 THEN VALUE1 ELSE 0 END )
FROM TABLE
0

## Featured Post

### Suggested Solutions

As they say in love and is true in SQL: you can sum some Data some of the time, but you can't always aggregate all Data all the time! Introduction: By the end of this Article it is my intention to bring the meaning and value of the above quote to…
This article describes how to use the timestamp of existing data in a database to allow Tableau to calculate the prior work day instead of relying on case statements or if statements to calculate the days of the week.
To add imagery to an HTML email signature, you have two options available to you. You can either add a logo/image by embedding it directly into the signature or hosting it externally and linking to it. The vast majority of email clients display l…
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…