SfSkills Domains Docs GitHub

Home / apex / soql-aggregate-field-type-support

soql-aggregate-field-type-support

Use when you need to know which of the six SOQL aggregate functions (AVG, COUNT, COUNT_DISTINCT, MIN, MAX, SUM) a given Salesforce field type supports — e.g.

Open the skill folder on GitHub


SOQL Aggregate Field-Type Support

This skill activates when a practitioner needs to know which SOQL aggregate functions apply to which field types. Salesforce publishes a dedicated reference page for exactly this because the six functions "aren't relevant for all field types" — SUM() and AVG() are numeric-only, several field types support no aggregate at all, and a few (currency, picklist) carry non-obvious semantics. The payoff is catching a SUM(CloseDate)-style mistake at authoring time instead of as a runtime query error.


Before Starting

Gather this context before working on anything in this domain:


Core Concepts

The six functions and what each returns

FunctionReturnsField-type requirement
AVG(field)Average value of a numeric fieldNumeric only
SUM(field)Total sum of a numeric fieldNumeric only
COUNT() / COUNT(fieldName)Number of rows matching the queryBroad (see matrix)
COUNT_DISTINCT(field)Number of distinct non-null valuesBroad (see matrix)
MIN(field)Minimum value of a fieldBroad (see matrix)
MAX(field)Maximum value of a field (API 18.0+)Broad (see matrix)

The compatibility matrix (the reference this skill exists for)

Field type(s)AVGSUMCOUNTCOUNT_DISTINCTMINMAX
Numeric — int, double, currency, percentYesYesYesYesYesYes
Date/time — date, dateTimeNoNoYesYesYesYes
Text-like — reference/lookup, ID, email, phone, url, textarea, picklist, combobox, DataCategoryGroupReferenceNoNoYesYesYesYes
No aggregate support — base64, boolean, time, multipicklist, address, location, encryptedstringNoNoNoNoNoNo
Calculated (formula)Depends on the formula's return type — apply the row for that type

Two behaviors do not fit in a cell:

Null handling — COUNT() is the exception

All aggregate functions ignore null values except COUNT() and COUNT(Id). Practical consequence:

So COUNT(Id) vs COUNT(Email) on the same result set can legitimately differ — the gap is the number of records with a blank Email.

Formula fields resolve by return type

A calculated (formula) field has no fixed rule: "Support for aggregate functions depends on the type of the calculated field." A formula returning Currency behaves like currency (all six); a formula returning Text behaves like text (no AVG/SUM). Determine the formula's return type first, then apply the matching matrix row.


Common Patterns

Earliest / latest instead of "average date"

When to use: you need a date summary across a group but reached for AVG()/SUM() out of SQL habit.

How it works: date and dateTime support MIN() and MAX() (and the counts), so use MIN(CreatedDate) for the earliest and MAX(CreatedDate) for the latest. There is no average or sum of dates — the query will error if you try.

Why not the alternative: AVG(CreatedDate) is not "unsupported but harmless" — it fails the query outright, breaking the whole request, not just that column.

Multi-currency aggregate on a currency field

When to use: you SUM() or AVG() an Amount-style currency field in a multi-currency org.

How it works: the aggregate result defaults to the corporate (system) currency, so a raw SUM(Amount) blends converted values into one number. To keep each subtotal in a single, unambiguous currency, GROUP BY CurrencyIsoCode:

SELECT CurrencyIsoCode, SUM(Amount) total
FROM Opportunity
GROUP BY CurrencyIsoCode

Why not the alternative: an ungrouped SUM(Amount) in a mixed-currency org produces a corporate-currency figure that few stakeholders expect and none can trace back to a currency; the grouped form is auditable.

Count all rows vs count populated values

When to use: you're reporting "how many" and need to be precise about nulls.

How it works: use COUNT(Id) (or COUNT()) for the total record count; use COUNT(field) or COUNT_DISTINCT(field) when you specifically want the count of records where field is populated (or the count of unique values). Keep the SOQL inside a selector method — see 5 — rather than inline in a trigger or controller.


Decision Guidance

SituationRecommended ApproachReason
Need a total or average of a numberSUM() / AVG() — but only on int, double, currency, or percentOnly fully numeric types support AVG/SUM
Need earliest / latest of a date or dateTimeMIN() / MAX()Dates support MIN/MAX/COUNT but not AVG/SUM
Field is boolean, time, multipicklist, base64, address, location, or encryptedstringRestructure — derive a numeric or supported field firstNone of the six functions apply to these types
Need a distinct count of a text or picklist fieldCOUNT_DISTINCT(field) (ignores nulls)Text-like types support the counts, MIN, and MAX
Aggregating a formula fieldLook up the formula's return type, then apply that type's matrix rowSupport "depends on the type of the calculated field"
Summing a currency field in a multi-currency orgGROUP BY CurrencyIsoCode; know the ungrouped result is corporate currencyCurrency aggregates default to the system currency
Ordering a picklist with MIN/MAXExpect picklist sort order, not alphabeticalMIN/MAX use the picklist's defined value order

Step-by-step instructions for an AI agent or practitioner working on this task:

  1. Enumerate the fields and their exact data types. For each field you want to aggregate, record its API name and type (Object Manager or a describe). For formula fields, record the return type.
  2. Look up each (type × function) cell in the compatibility matrix. If the cell is "No," the query will fail when parsed or executed — do not ship it.
  3. Swap or refactor unsupported combinations. Replace AVG()/SUM() on non-numeric fields with MIN()/MAX()/COUNT() as appropriate, or derive a numeric field upstream when you truly need a total.
  4. Choose COUNT semantics deliberately. Use COUNT(Id)/COUNT() for total rows; use COUNT(field)/COUNT_DISTINCT(field) when nulls should be excluded.
  5. Handle currency and picklist semantics. For multi-currency currency aggregates, decide whether to GROUP BY CurrencyIsoCode; for MIN()/MAX() on picklists, confirm picklist sort order gives the value you expect.
  6. Validate before shipping. Run scripts/check_soql_aggregate_field_type_support.py over the query (optionally with a field-type map) and confirm it in the Developer Console Query Editor.

Review Checklist

Run through these before marking work in this area complete:


Salesforce-Specific Gotchas

Non-obvious platform behaviors that cause real production problems:

  1. AVG()/SUM() on a non-numeric field fails the query — date, text, picklist, and boolean fields support only the counts and (where applicable) MIN/MAX. The whole request errors; it does not degrade gracefully.
  2. Seven types support no aggregate at all — base64, boolean, time, multipicklist, address, location, and encryptedstring. No function, including COUNT, works on them; you must derive a supported field.
  3. Currency aggregates default to the system (corporate) currency in a multi-currency org — an ungrouped SUM(Amount) is not "the sum of what each record shows."
  4. MIN()/MAX() on a picklist uses picklist sort order, not alphabetical — the "minimum" stage is the first one in Setup's ordering, which can surprise anyone expecting A–Z.
  5. COUNT(field) quietly ignores nulls — unlike COUNT()/COUNT(Id), so a populated-field count can be lower than the row count without any error to signal it.

Output Artifacts

ArtifactDescription
templates/soql-aggregate-field-type-support-template.mdThe full compatibility matrix plus a per-query pre-flight worksheet to fill in for your fields
scripts/check_soql_aggregate_field_type_support.pyStdlib checker that flags unsupported (field type × function) combinations and the LIMIT-without-GROUP-BY error in a SOQL string or file

Package files