SOQL SELECT DISTINCT: How to Get Unique Values in Salesforce

By: Rajeshwari Jain | Published: July 29, 2026 | 11 min
SOQL Select Distinct

Unlike SQL, SOQL doesn’t support the SELECT DISTINCT keyword. Attempting to use it results in a syntax error. To retrieve unique values from a Salesforce field, use GROUP BY instead, which returns each distinct value once. This guide explains why SELECT DISTINCT isn’t available in SOQL, how to use GROUP BY correctly, and how to retrieve unique values from Salesforce data.

Why SELECT DISTINCT Doesn't Work in SOQL

Standard SQL databases support the SELECT DISTINCT clause to remove duplicate values from a query result.

For example:

SQL
SELECT DISTINCT Industry FROM Account

However, SOQL doesn’t support the DISTINCT keyword. Running the same query in Salesforce results in a syntax error because DISTINCT isn’t part of the SOQL language.

Developer Console Query Editor showing SELECT DISTINCT Industry FROM Account query returning an "only aggregate expressions use field aliasing" error.

To retrieve unique values in SOQL, use the GROUP BY clause on the field you want to deduplicate.

SQL
SELECT Industry
FROM Account
GROUP BY Industry

The GROUP BY query returns each industry once, regardless of how many Account records share the same value. For example, if your org contains 1,000 accounts across 28 industries, the query returns 28 rows. Records where the grouped field is null are returned as a separate group. If some Account records don’t have an Industry value, the query returns one additional row representing the null group.

Developer Console showing GROUP BY Industry query on Account, returning 28 unique Industry values as separate rows.

This is one of the most common SQL-to-SOQL differences. While SQL uses DISTINCT to eliminate duplicate values, SOQL uses aggregate queries with GROUP BY to achieve a comparable result.

Getting Distinct Values from a Single Field

The most common use case is retrieving the unique values stored in a single field, such as industries, opportunity stages, account types, or regions. Salesforce administrators and developers often use this technique to audit data, validate field values, or prepare for reporting and data migrations.

Unlike standard SOQL queries, a query that uses GROUP BY returns a list of AggregateResult objects in Apex rather than a list of sObjects.

SQL
SELECT Industry
FROM Account
GROUP BY Industry

This query returns one row for each unique Industry value found on the Account records visible to the running user.

Distinct opportunity stages

SQL
SELECT StageName
FROM Opportunity
GROUP BY StageName

This query returns each unique Opportunity stage once. It’s useful for quickly identifying the stage values currently in use in your org.

Developer Console showing SELECT StageName FROM Opportunity GROUP BY StageName query returning 10 unique Opportunity stages.

Filter before deduplicating

SQL
SELECT Industry
FROM Account
WHERE BillingCountry = 'USA'
GROUP BY Industry

This query returns unique Industry values only for accounts whose BillingCountry is USA. Salesforce applies the WHERE clause first and then groups the filtered records to produce distinct values.

Developer Console showing SOQL query with WHERE BillingCountry = 'USA' and GROUP BY Industry returning 8 unique Industry values.

Getting Distinct Values from Multiple Fields

GROUP BY can include multiple fields. Separate the field names with commas to return each unique combination of values.

This is useful when you need combinations such as:

  • Industry and Billing Country
  • Region and Status
  • Lead Source and Owner

Example:

SQL
SELECT Origin, Priority
FROM Case
WHERE IsClosed = false
GROUP BY Origin, Priority

This returns one row for each unique Origin and Priority combination. If 250 open cases came in by Email with High priority, they collapse into a single row for that pair.

Developer Console showing query with GROUP BY Origin, Priority on open Cases, returning 5 unique combinations.

Adding a count makes the grouping visible:

SQL
SELECT Origin, Priority, COUNT(Id) caseCount
FROM Case
WHERE IsClosed = false
GROUP BY Origin, Priority
ORDER BY COUNT(Id) DESC
Developer Console showing query with GROUP BY Origin, Priority and ORDER BY COUNT(Id) DESC, returning 5 groups sorted by case count.

Note

  • Every field in the SELECT clause must either appear in the GROUP BY clause or be used in an aggregate function such as COUNT(fieldName), COUNT_DISTINCT(), SUM(), MIN(), MAX(), or AVG(). Parameterless COUNT() can’t be combined with GROUP BY; use COUNT(Id) instead.

  • Not every field can be grouped. Long Text Area, Rich Text Area, encrypted fields, and some formula fields aren’t groupable. Check DescribeFieldResult.isGroupable() before building a dynamic query.

  • Records with a blank value form their own group, where the grouped field is returned as null. Use WHERE Industry != NULL to exclude them.

Warning icon

Tip

GROUP BY ROLLUP and GROUP BY CUBE add subtotal and grand total rows to grouped results.

Sorting Distinct Values

You can use ORDER BY with a GROUP BY query to sort grouped results.

The following query returns distinct industries in ascending order:

SQL
SELECT Industry
FROM Account
GROUP BY Industry
ORDER BY Industry ASC
Developer Console showing GROUP BY Industry with ORDER BY ASC, returning 28 unique Industry values sorted alphabetically.

You can also sort by an aggregate value:

SQL
SELECT Industry, COUNT(Id)
FROM Account
GROUP BY Industry
ORDER BY COUNT(Id) DESC

This query sorts industries by the number of Account records in each group, from highest to lowest.

Developer Console showing GROUP BY Industry with COUNT(Id) sorted DESC, returning 28 industries ranked by Account count.
Warning icon

Tip

Use NULLS FIRST or NULLS LAST to control where null values appear.

Distinct Values in Apex

You can retrieve distinct values in Apex by:

  • Using a SOQL query with GROUP BY.
  • Querying records and removing duplicates with an Apex Set.

Use GROUP BY when you only need distinct values.

Option 1: Use GROUP BY (Recommended)

SQL
AggregateResult[] results = [
    SELECT Industry
    FROM Account
    WHERE Industry != NULL
    GROUP BY Industry
];

System.debug('Number of unique industries: ' + results.size());

for (AggregateResult ar : results) {
    System.debug('Industry: ' + ar.get('Industry'));
}

This query returns one AggregateResult for each unique Industry.

Apex debug log showing USER_DEBUG output with 27 unique industries listed after processing AggregateResult in a for loop.
Note icon

Note

  1. Aggregate queries return a maximum of 2,000 rows and don’t support queryMore(). If a query can produce more grouped results, narrow the data with a WHERE filter or group on fewer fields.
  2. Each AggregateResult counts as one row toward the 50,000 query rows governor limit.

Warning icon

Tip

  1. Assign aliases to aggregate expressions for easier access. For example, COUNT(Id) accountCount can be retrieved with ar.get(‘accountCount’). Without an alias, Salesforce uses generated names such as expr0.

  2. ar.get() returns an Object. Cast it to the appropriate type before using it. For example, COUNT() returns an Integer, while SUM() and AVG() return a Decimal.

Option 2: Use an Apex Set

SQL
Set<String> uniqueIndustries = new Set<String>();
for (Account a : [
    SELECT Industry
    FROM Account
    WHERE Industry != NULL
    LIMIT 50000
]) {
    uniqueIndustries.add(a.Industry);
}
System.debug(uniqueIndustries);
System.debug('Size: ' + uniqueIndustries.size());

A Set stores only unique values, so duplicate Industry values are automatically discarded as records are added.

Apex debug log showing Set of 27 unique industries with size, deduplicated from Account records via Apex Set collection.
Warning icon

Tip

A SOQL for loop processes records in batches, which helps reduce heap usage compared with loading all records into a List.

Use GROUP BY when you only need distinct values. Use an Apex Set when you need to process individual records before removing duplicates.

To retrieve distinct values and record counts in a single query, use:

SQL
SELECT Industry, COUNT(Id)
FROM Account
GROUP BY Industry

Counting Unique Values

If you need to count how many unique values exist in a field instead of listing each one, use the COUNT_DISTINCT() aggregate function.

SQL
SELECT COUNT_DISTINCT(Industry)
FROM Account
Developer Console showing SELECT COUNT_DISTINCT(Industry) FROM Account returning 27 as count of unique Industry values.

This returns a single integer – the number of unique Industry values across all accounts. For a full explanation of COUNT_DISTINCT(), including aliases, use with GROUP BY, and supported field types, see our SOQL COUNT guide.

Common Limitations

NULL Values Appear as a Separate Group

When you use GROUP BY, Salesforce treats NULL as a separate group. This allows you to retrieve every distinct field value, including records where the field has no value.

SQL
SELECT Industry
FROM Account
GROUP BY Industry

If some Account records don’t have an Industry value, the query returns an additional row representing the NULL group.

To exclude records with NULL values, filter them in the WHERE clause before grouping.

SQL
SELECT Industry
FROM Account
WHERE Industry != null
GROUP BY Industry

Unlike SQL, SOQL doesn’t support the IS NULL or IS NOT NULL operators. Instead, use = null or != null.

If you need to filter grouped results based on an aggregate value, use the HAVING clause.

SQL
SELECT Industry, COUNT(Id)
FROM Account
WHERE Industry != null
GROUP BY Industry
HAVING COUNT(Id) > 5

In this example, the WHERE clause excludes records with NULL values before grouping, while the HAVING clause returns only industries that have more than five matching accounts.

GROUP BY Doesn't Support All Field Types

Not every Salesforce field can be used in a GROUP BY clause. Common examples of unsupported field types include:

  • Long Text Area
  • Rich Text Area
  • Multi-Select Picklist
  • Encrypted fields

If you’re unsure whether a field supports grouping, check its isGroupable() property by using the Schema Describe API.

SQL
System.debug(
    Schema.SObjectType.Account.fields.Industry
        .getDescribe()
        .isGroupable()
);

If the method returns true, the field can be used in a GROUP BY clause. If it returns false, Salesforce doesn’t allow grouping on that field.

Getting Full Records of Distinct Values Isn't Directly Possible

A GROUP BY query returns one row for each unique group, along with any aggregate values that you specify. Aggregate queries return AggregateResult objects rather than sObjects, so they can’t return the complete records that belong to each group.

If you need the full record details, use one of these approaches:

  • Run a GROUP BY query to identify the distinct values, then execute a second SOQL query to retrieve the matching records.

  • Query the records directly and derive the distinct values in Apex or another application layer.

Aggregate Result Limit

A SOQL query that uses GROUP BY can return a maximum of 2,000 grouped results. Aggregate queries don’t support queryMore(). If the query produces more than 2,000 groups, you must reduce the number of results returned.

To work within this limit:

  • Add WHERE filters to reduce the number of groups.

  • Split the query into multiple smaller queries by using a selective field, such as owner, record type, or date range.

  • Process the results from those smaller queries in Apex or an external application if you need to analyze a larger data set.

Running Distinct Queries in Excel and Google Sheets

If you work in Excel or Google Sheets, you can run SOQL queries without opening the Developer Console or writing Apex. XL-Connector (for Excel and Excel Online) and G-Connector (for Google Sheets) let you paste a SOQL query directly into a spreadsheet and return the results. To get unique values, use the same GROUP BY query you would use in Salesforce.

It is useful for extracting distinct values from picklist or text fields to review data quality, identify inconsistent values, or build dropdown lists. Learn more about working with SOQL queries in spreadsheets.

Conclusion

Although SOQL doesn’t support the SELECT DISTINCT keyword, you can still retrieve unique values by using the GROUP BY clause. If you need to count unique values, use COUNT_DISTINCT(). If you need to work with individual records before removing duplicates, use an Apex Set. Knowing when to use each approach, along with their limits and supported field types, helps you write better SOQL queries for reporting, data validation, integrations, and Apex development.

FAQ

Does SOQL support SELECT DISTINCT?

No. SOQL does not support the DISTINCT keyword. To retrieve unique values, use the GROUP BY clause on the field you want to return.


What's the easiest way to get unique values in SOQL?

Use GROUP BY on the field whose unique values you need. For example, SELECT Industry FROM Account GROUP BY Industry returns each distinct industry value once.


How do I get distinct values from multiple fields?

Include all required fields in both the SELECT and GROUP BY clauses. For example, SELECT Industry, BillingCountry FROM Account GROUP BY Industry, BillingCountry returns each unique combination of Industry and BillingCountry.


How do I count distinct values in SOQL?

Use the COUNT_DISTINCT() aggregate function. For example, SELECT COUNT_DISTINCT(Industry) FROM Account returns the number of distinct non-null Industry values. For more information, see our SOQL COUNT guide.


How do I get distinct values from a related (parent) field?

Use relationship dot notation in both the SELECT and GROUP BY clauses. For example, SELECT Account.Industry FROM Contact GROUP BY Account.Industry returns each distinct parent account industry referenced by contacts.


Can I use SELECT DISTINCT in Apex SOQL?

No. Apex uses the same SOQL syntax as Salesforce, so the DISTINCT keyword is not supported. Use GROUP BY in the SOQL query to return unique values, or retrieve the records and remove duplicates by storing the values in an Apex Set.

|
Rajeshwari Jain

Rajeshwari Jain

Content Manager

About the Author

Rajeshwari Jain is a Technical Support Specialist and Content Writer at Xappex. She applies her practical experience to assist customers and create articles on how Xappex tools work with Salesforce to improve data management and increase efficiency.

She began her IT career in 2022 as a Quality Assurance professional before transitioning into Salesforce administration and technical writing in 2023. With Salesforce Certified Administrator and Associate certifications, Rajeshwari writes blogs on Salesforce flows, admin tools, and updates to expand her skills outside of work.

In her free time, she enjoys reading tech blogs and experimenting with new tools.

Feel free to reach out to Rajeshwari for collaborations or to check out her Salesforce-focused content.