Polymorphic SOQL with TYPEOF: querying Task.WhatId, Task.WhoId, ContentDocumentLink.LinkedEntityId, FeedItem.ParentId; fallback to Type filters; indexing and selectivity. NOT for subqueries or dot-notation traversal — use apex/apex-soql-relationship-queries. NOT for the Activity data model — use admin/activity-and-task-patterns.
Installs into .claude/skills of the current project.
Are you the author of Apex Polymorphic Soql?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/pranavnagrecha-apex-polymorphic-soql)
---
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 subqueries or dot-notation traversal — use apex/apex-soql-relationship-queries. NOT for the Activity data model — use admin/activity-and-task-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"
- "soql typeof polymorphic what who task query"
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
status: stub
---
# 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. `WhereType` filter narrows to one type then accesses its fields normally.
- **Plan selectivity.** `WhatId = :id` is 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 Lead
- `ContentDocumentLink.LinkedEntityId` — any object with content enabled
- `FeedItem.ParentId` — any feed-enabled object
- `Note.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
1. Identify the polymorphic field and its possible target objects.
2. Decide per-type fields required.
3. If varied per type, use TYPEOF; if one type only, use Type filter.
4. Ensure Id or other selective filter is present — Type alone is non-selective.
5. Apex-side: iterate results and use `instanceof` or `.getSObjectType()` to dispatch.
6. Test with rows of each target type present.
7. 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()` or `instanceof` for dispatch
- [ ] Tests seed at least two target-type rows
- [ ] Query plan reviewed for polymorphic selectivity
## Salesforce-Specific Gotchas
1. **Not all polymorphic fields support TYPEOF.** Task, Event, ContentDocumentLink, FeedItem, Note do; verify for newer objects.
2. **`Schema.SObjectType.Task.fields.WhatId.getReferenceTo()`** returns the full list of possible target types at runtime.
3. **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 model
- `apex/apex-soql-patterns` — SOQL patterns generally
- `data/large-data-volume-patterns` — selectivity and indexing