# Apex Polymorphic Soql

> 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.

- Skill: `pranavnagrecha/apex-polymorphic-soql` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds add pranavnagrecha/apex-polymorphic-soql`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pranavnagrecha/apex-polymorphic-soql/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: PranavNagrecha (https://skillmd.com/u/pranavnagrecha)
- Updated: 2026-09-08
- Page: https://skillmd.com/skills/pranavnagrecha/apex-polymorphic-soql

---


# 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

