name: apex-polymorphic-soql description: "Polymorphic SOQL with TYPEOF: querying Task.WhatId, Task.WhoId, ContentDocumentLink.LinkedEntityId, FeedItem.ParentId; fallback to Type filters; indexing and selectivity. NOT for Activity object model (use activity-and-task-patterns). NOT for general SOQL (use apex-soql-patterns)." category: apex salesforce-version: "Spring '25+" well-architected-pillars:
- Performance
- Reliability tags:
- apex
- soql
- typeof
- polymorphic
- whatid
- linkedentityid triggers:
- "soql typeof what when account opportunity"
- "polymorphic lookup query salesforce what.name fails"
- "contentdocumentlink linkedentityid polymorphic query"
- "typeof where clause syntax soql"
- "polymorphic id type filter what.type"
- "apex query all attachments across object types" inputs:
- Polymorphic field and its possible target objects
- Fields needed per target type
- Selectivity / index requirements outputs:
- TYPEOF query template
- Type-filtered fallback queries
- Indexing strategy for polymorphic filters dependencies: [] version: 1.0.0 author: Pranav Nagrecha updated: 2026-04-21
Apex Polymorphic SOQL
Activate when writing SOQL against polymorphic fields — Task.WhatId, Task.WhoId, Event.WhatId, ContentDocumentLink.LinkedEntityId, FeedItem.ParentId, Note.ParentId. Polymorphic fields reference multiple object types; field access requires TYPEOF or Type filters, and the platform's indexing rules are different from normal lookup fields.
Before Starting
- Know which fields are polymorphic. Not many: Task/Event WhatId/WhoId, ContentDocumentLink.LinkedEntityId, FeedItem.ParentId, Note.ParentId, EmailMessageRelation.RelationId, a few others.
- Decide: TYPEOF or Type filter? TYPEOF returns per-type projected fields in one query.
WhereTypefilter narrows to one type then accesses its fields normally. - Plan selectivity.
WhatId = :idis selective;What.Type = 'Account'is a filter, not an index.
Core Concepts
TYPEOF syntax
SELECT Id, Subject,
TYPEOF What
WHEN Account THEN Name, Industry
WHEN Opportunity THEN Amount, StageName
ELSE Name
END
FROM Task
WHERE ActivityDate = TODAY
Per-row, the query returns fields of the matched target type. ELSE is optional — include it to cover unmatched cases.
Type filter fallback
SELECT Id, What.Name FROM Task WHERE What.Type = 'Account'
Post-filter, What.Name is safe because every match is an Account. Less flexible than TYPEOF but simpler.
Common polymorphic fields
Task.WhatId— Account, Opportunity, custom, ...Task.WhoId— Contact or LeadContentDocumentLink.LinkedEntityId— any object with content enabledFeedItem.ParentId— any feed-enabled objectNote.ParentId/NoteAndAttachment.ParentId— legacy
Indexing
Polymorphic field equality on Id is selective (indexed). Filtering on .Type is a non-indexed filter — apply after an Id-selective filter.
Common Patterns
Pattern: TYPEOF with common-parent field
SELECT Id, Subject, What.Name, What.Type FROM Task WHERE Id IN :ids
What.Name works because Name is on every polymorphic target's parent. Avoids TYPEOF ceremony for common fields.
Pattern: TYPEOF for type-specific fields
Use when you need Industry (Account), Amount (Opportunity), etc. Only TYPEOF can project per-type fields.
Pattern: Two-step query
Query IDs and types; partition; re-query per type with full field lists. Useful when downstream logic per type is complex.
Decision Guidance
| Need | Pattern |
|---|---|
| Just Id, Type, Name | Flat query with What.Name |
| Per-type fields in one pass | TYPEOF WHEN |
| One specific type only | WHERE What.Type = 'Account' |
| Complex per-type processing | Query once, partition in Apex, re-query per type |
Recommended Workflow
- Identify the polymorphic field and its possible target objects.
- Decide per-type fields required.
- If varied per type, use TYPEOF; if one type only, use Type filter.
- Ensure Id or other selective filter is present — Type alone is non-selective.
- Apex-side: iterate results and use
instanceofor.getSObjectType()to dispatch. - Test with rows of each target type present.
- Measure query plan if performance-critical.
Review Checklist
- Query uses TYPEOF or Type filter for per-type fields
- Selective filter (Id, date range) present
- ELSE branch on TYPEOF handles unmapped types
- Apex iteration uses
getSObjectType()orinstanceoffor dispatch - Tests seed at least two target-type rows
- Query plan reviewed for polymorphic selectivity
Salesforce-Specific Gotchas
- Not all polymorphic fields support TYPEOF. Task, Event, ContentDocumentLink, FeedItem, Note do; verify for newer objects.
Schema.SObjectType.Task.fields.WhatId.getReferenceTo()returns the full list of possible target types at runtime.- TYPEOF cannot be used in subqueries (inner queries within SELECT list). Plan accordingly.
Output Artifacts
| Artifact | Description |
|---|---|
| Polymorphic query library | Reusable TYPEOF queries per field |
| Target-type catalog | Field → list of possible objects |
| Dispatcher helper | Apex utility routing by record type |
Related Skills
admin/activity-and-task-patterns— Activity object modelapex/apex-soql-patterns— SOQL patterns generallydata/large-data-volume-patterns— selectivity and indexing