Guide · The plan, node by node
Rows Removed by Filter
Rows Removed by Filter is not a node. It is a line under a scan in EXPLAIN ANALYZE output,
counting the rows the node read and then rejected because they failed its Filter. The
sample's Seq Scan on commerce.orders reads 12,000,000 rows, keeps 206,880 and prints Rows Removed by Filter: 11793120. Those 11,793,120 rows were fetched, tested against channel = 'web' AND placed_at >= ..., and dropped.
Why it matters
It is the clearest measure of wasted work in a plan. Rows kept over rows read says how well
the access path fits the query. orders keeps 1.7%. Under an Index Scan,
a Filter line beneath the Index Cond means the index found rows the query then rejected.
The rest of the WHERE is checked after the fetch, and a large count there is a missing
column. The figure is per loop, so a scan with loops=206880 and Rows Removed by Filter: 2
removed 413,760 rows. Loops has the arithmetic.
What to do
- On a Seq Scan removing most of the table, index the filter columns,
equality first. The sample's fix is
CREATE INDEX orders_channel_placed_idx ON commerce.orders (channel, placed_at DESC). - On an Index Scan with a
Filter, add the filtered column to the index so the check moves into theIndex Cond. - If the filter is an expression,
lower(email) = $1, index the expression:CREATE INDEX ON crm.customers (lower(email)).
In the Plan tab
The removed rows drain off below the scan's band. The drain is marker 3. The rest of the flow is there so you can see what the drain took from.
The row flow of the sample, with the filter's drain under Seq Scan on orders.
The row flow of the sample, with the filter's drain under Seq Scan on orders.
- The band into the scan is the 12,000,000 rows it read, every one of them tested against the filter.
207K ┅ 612is what survived, 206,880 rows, beside the planner's guess of 612. The band that continues to the right is the 1.7% kept.−11.8MisRows Removed by Filter, drained off below the scan: 11,793,120 rows fetched, tested and dropped. A drain this wide against a band this thin is the shape of a missing index.↻ ×207K ┅ ×612is how many times theorder_itemsscan ran. ARows Removed by Filteron a looped scan is per run, so it would be multiplied by this.1.64 sis the scan's self time, most of it spent on rows it then threw away.- The result end, off to the right, is where the rows that survived every filter leave the plan.
The inspector's Rows bar draws read against kept with 2% kept, and the table wheel's
guide lists Rows read and Rows kept.
Caveats
- On a Bitmap Heap Scan the drain adds
Rows Removed by Index Recheckto the count.