Not verified hands-on. This article was written from primary documentation rather than reproduced in an org, so it carries no tested-release badge. If you run it and something differs, say so in the comments and it gets tested and corrected.

Straight answer: the Query Plan tool shows you which index the optimiser would use, and a cost number where anything above 1.0 means no index is being used well. It lives in the Developer Console behind a setting you have to turn on, and reading it correctly is mostly about understanding that cost is a ratio, not a duration.

Turning it on

Developer Console, then Help, then Preferences, then Enable Query Plan. Nothing indicates it exists until you do this. After that, the Query Editor tab shows a Query Plan button next to Execute.

Reading the output

You get one row per plan the optimiser considered, ordered by cost, with the one it would choose first. The columns that matter:

  • Cost is the ratio of the estimated cost of this plan to the cost of a full table scan. Below 1.0 means better than a scan. Above 1.0 means worse. A cost of 0.02 is excellent. A cost of 2.4 means the optimiser thinks using this path would be worse than reading everything.
  • Leading Operation Type is one of Index, TableScan, Sharing or Other. TableScan as the chosen plan is the finding.
  • sObject Cardinality is the optimiser’s estimate of total rows on the object.
  • Relative Cost and sObject Type give context for the above.
  • Fields, which field the index is on. Blank for a table scan.

The number is an estimate produced from statistics the platform maintains, not a measurement of your query. It will not tell you how long the query took.

The thresholds behind the number

An index is used only when the filter is selective enough. The rule is a proportion of rows:

  • Standard index: the filter must match under 30% of the first million records, and under 15% of anything beyond that, capped at 1 million rows total.
  • Custom index: under 10% of the first million, under 5% beyond, capped at 333,333 rows.

So a field with only two values is never selective on a large object, no matter how many indexes you add. A Status field where 60% of records are Closed cannot be indexed usefully for finding closed records, and can be for finding the rare ones.

What makes a filter non-indexable at all

Some filters cannot use an index regardless of selectivity, and the plan will show a table scan:

  • != and NOT
  • LIKE '%value', a leading wildcard
  • Comparisons against null on a field with no index that includes nulls
  • Filters on formula fields, unless the formula is deterministic and has been indexed
  • OR across different fields, unless every branch is independently indexed

The OR one surprises people most. Two indexed fields joined by OR frequently produces a scan, and splitting into two queries with a union in Apex can be dramatically faster.

Using it properly

Run the plan before and after each change, not once at the end. The useful loop is:

  1. Plan the query as written. Record the cost and the leading operation.
  2. Remove the filter you suspect. Plan again.
  3. If the cost drops sharply, that filter is the problem.
  4. Check whether the field is indexed at all — Object Manager shows an Indexed column on the field list.

This is the same elimination approach as diagnosing a slow report, with the advantage that here you can actually see the plan.

Its limits

It plans the query you type, as the user you are. It does not account for the sharing calculation a different user would trigger, and a Sharing leading operation type is a hint that sharing dominates the cost, which is a different problem with a different fix, usually skew.

It also does not exist for reports. Reports use the same optimiser and give you no visibility, which is why report diagnosis is done by elimination instead.

Frequently asked

Can I get a plan from Apex or the CLI? Yes, through the REST endpoint /services/data/vXX.0/query/?explain=..., which returns the same data as JSON and is scriptable.

Does a cost under 1.0 mean the query is fast? It means an index will be used. A query returning 400,000 indexed rows is still slow.

How do I get a custom index? Ask Salesforce Support. External ID and unique fields get one automatically, which is often the cheapest route.