{
  "id": 18058841,
  "title": "How to read PostgreSQL EXPLAIN output",
  "url": "https://urgent.news/2026/11/04/how-to-read-postgresql-explain-output",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-11-04T02:58:21.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/database-engineering/how-to-read-postgresql-explain-output-4bhd"
  },
  "original_language": "en",
  "account": "PostgreSQL EXPLAIN output helps you understand the query execution plan by representing it as a tree of nodes. Each node represents a retrieval or processing step, along with estimated cost, row count, and row width. The plan is read from leaves to the root, and execution order varies depending on the algorithm used.\n\nIn plain EXPLAIN, the plan starts with the final result at the top, followed by child nodes marked with indentation. Scans sit at the leaves, while joins, sorts, and aggregates appear higher up the tree. In text output, Filter and Index Cond describe nodes instead of forming new ones.\n\nThe cost, rows, and width values in the plan are estimated by the planner based on statistics and configurable cost parameters. They do not represent actual measurements but rather predictions based on available data. These fields are essential for comparing the predicted plan with the actual execution.\n\nTo properly diagnose query performance issues, it's crucial to understand the meaning of cost, rows, and width. Costs are the planner's predictions, not measurements, and are based on models using configurable parameters like seq_page_cost and random_page_cost. Rows and width are estimates based on sampled statistics from pg_class, pg_statistic, and other sources.\n\nWhen investigating a row-count mismatch, first check if the parent node like LIMIT stopped the child early. If the discrepancy occurs in a node that ran to completion, it warrants further investigation. Compare the estimated and actual rows to identify where the estimates first diverge. Analyze the relevant statistics, such as most_common_vals, histogram_bounds, and n_distinct in pg_stats, to understand the cause of the mismatch.\n\nKeep in mind that one EXPLAIN plan provides insights into what PostgreSQL chose and what happened during that single execution. It cannot establish why the plan was slow or if the fix generalizes to other situations. Therefore, always start with plain EXPLAIN when execution consequences are unknown and use EXPLAIN ANALYZE to see the actual execution details.",
  "summary": "TL;DR Read the plan tree from leaves to root; cost, rows, and width are estimates, not measurements. Start with plain EXPLAIN when execution consequences are unknown. EXPLAIN ANALYZE executes the statement and adds observed time, rows, and loops. Before diagnosing a row-count mismatch, check for early stops and loops. A substantial mismatch in a node that ran to completion warrants investigation.…",
  "key_points": [],
  "editors_take": null,
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}