---
title: "Execution plan"
description: "An execution plan is the tree of operators a database will run, in a specific order, to produce the result of a query — the concrete strategy chosen by the query planner and optimizer."
canonical: "https://motherduck.com/glossary/execution-plan/"
related:
  - title: "EXPLAIN | MotherDuck Docs"
    url: "https://motherduck.com/docs/sql-reference/motherduck-sql-reference/explain/"
  - title: "DuckLake Architecture Deep Dive"
    url: "https://motherduck.com/blog/ducklake-architecture-deep-dive/"
  - title: "Optimizing query performance | MotherDuck Docs"
    url: "https://motherduck.com/docs/key-tasks/query-performance/"
gated_asset:
  title: "DuckLake: The Lakehouse Table Format"
  url: "https://motherduck.com/lp/ducklake-lakehouse-table-format-book-full/"
---

# Execution plan

> An execution plan is the tree of operators a database will run, in a specific order, to produce the result of a query — the concrete strategy chosen by the query planner and optimizer.

## Overview

Every SQL query is ultimately executed as a sequence of lower-level operations: scan this table, apply this filter, join with that table, aggregate these groups, project these columns. An execution plan lays these operations out as a tree, typically read bottom-up, showing exactly what work the database will do and in what order.

## Reading a Plan

A simple filtered join might produce a plan like:

```
PROJECTION
  HASH_JOIN (INNER)
    SEQ_SCAN (orders) [Filter: order_date >= '2024-01-01']
    SEQ_SCAN (customers)
```

Reading bottom-up: scan `orders` applying the date filter, scan `customers`, join the results, then project the requested columns. Real plans are often deeper, with multiple joins, aggregations, and sort steps.

## Viewing Plans in DuckDB

DuckDB exposes its execution plan with `EXPLAIN`, and augments it with actual runtime statistics via `EXPLAIN ANALYZE`:

```sql
EXPLAIN ANALYZE
SELECT c.region, sum(o.total)
FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE o.order_date >= '2024-01-01'
GROUP BY c.region;
```

`EXPLAIN ANALYZE` runs the query and shows, per operator, wall-clock time along with the estimated cardinality (EC) versus the actual number of rows produced. A large gap between estimated and actual cardinality on an operator is often the first clue that stale statistics are causing the optimizer to pick a suboptimal join order or algorithm — and is exactly the kind of thing `VACUUM ANALYZE` can help fix by refreshing those statistics.
