Use node types in an AlloyDB for PostgreSQL execution plan to help you understand how the database system processes a query. Each node represents a specific operation or step in the query execution, and their types provide insights into your chosen strategy.
Understanding the nodes of an explain plan is important for performance tuning. Each node accomplishes a different operation and obtains data differently, so each type can have a performance implication. For more information, see Generate and analyze execution plans .
Table and index node types
Table and Index related node types are important for understanding data access strategies.
Seq Scan
A Seq Scan node scans every row of a table. It doesn't allow filtering or
direct access to specific rows, unlike an index. For an example of a Seq Scan
,
see WAL option
.
Bitmap Heap Scan
A Bitmap Heap Scan node works with a Bitmap Index Scan node, which prefetches heap blocks based on the associated index scan. This node type is useful when a query returns a large number of rows, because the heap can undergo prefetch. In open source PostgreSQL versions 18 and earlier, Bitmap Index Scan is the only scan that produces a prefetch. Index Scan and Index Only Scan don't produce prefetches.
Bitmap Index Scan
A Bitmap Index Scan node precedes a Bitmap Heap Scan. The Bitmap Index Scan node scans the index, finds all matches, and builds a bitmap. It then passes this bitmap to the Bitmap Heap Scan to return the relevant data.
This bitmap allows page skipping
and prefetch, which results in faster data access. work_mem
is a useful tuning
lever for this scan type because it's required to build the bitmap. If work_mem
is too small, the bitmap stores only the entire
page in the bitmap rather than the exact row counter.
You can identify inefficient bitmap scans when the keyword "Lossy" is
on the bitmap scan node. To prevent this situation, increase work_mem
or
make sure that a recent vacuum
is completed on the table.
(
postgres
@
10
.
3
.
1
.
17
:
5432
)
[
postgres
]
>
EXPLAIN
(
ANALYZE
,
VERBOSE
,
COLUMNAR_ENGINE
,
COSTS
,
SETTINGS
,
BUFFERS
,
WAL
)
SELECT
*
FROM
public
.
effective_io_concurrency_test
eict
WHERE
id
BETWEEN
10000
AND
100000
OR
product_id
BETWEEN
100
AND
200
;
QUERY
PLAN
Bitmap
Heap
Scan
on
public
.
effective_io_concurrency_test
eict
(
cost
=
10318
.
30
..
488198
.
00
rows
=
619435
width
=
31
)
(
actual
time
=
113
.
696
..
2443
.
712
rows
=
589389
loops
=
1
)
Output
:
id
,
value
,
product_id
,
effective_date
Recheck
Cond
:
(((
eict
.
id
> =
10000
)
AND
(
eict
.
id
< =
100000
))
OR
((
eict
.
product_id
> =
100
)
AND
(
eict
.
product_id
< =
200
)))
Heap
Blocks
:
exact
=
378308
Buffers
:
shared
hit
=
1784
read
=
379535
written
=
4115
I
/
O
Timings
:
shared
read
=
1201
.
096
write
=
37
.
558
-
>
BitmapOr
(
cost
=
10318
.
30
..
10318
.
30
rows
=
619925
width
=
0
)
(
actual
time
=
68
.
895
..
68
.
896
rows
=
0
loops
=
1
)
Buffers
:
shared
hit
=
501
read
=
2510
I
/
O
Timings
:
shared
read
=
11
.
655
-
>
Bitmap
Index
Scan
on
effective_io_concurrency_test_pkey
(
cost
=
0
.
00
..
1807
.
88
rows
=
92951
width
=
0
)
(
actual
time
=
2
.
513
..
2
.
513
rows
=
90001
loops
=
1
)
Index
Cond
:
((
eict
.
id
> =
10000
)
AND
(
eict
.
id
< =
100000
))
Buffers
:
shared
hit
=
496
-
>
Bitmap
Index
Scan
on
effective_io_concurrency_test_groupby
(
cost
=
0
.
00
..
8200
.
71
rows
=
526974
width
=
0
)
(
actual
time
=
66
.
380
..
66
.
380
rows
=
499871
loops
=
1
)
Index
Cond
:
((
eict
.
product_id
> =
100
)
AND
(
eict
.
product_id
< =
200
))
Buffers
:
shared
hit
=
5
read
=
2510
I
/
O
Timings
:
shared
read
=
11
.
655
Settings
:
effective_cache_size
=
'19690120kB'
,
random_page_cost
=
'1.1'
,
work_mem
=
'256MB'
Query
Identifier
:
-
5140071079400709055
Planning
:
Buffers
:
shared
hit
=
36
Planning
Time
:
0
.
133
ms
Execution
Time
:
2477
.
216
ms
AlloyDB
query
id
:
18229116469546507386
AlloyDB
plan
id
:
17462269545806790969
Index Scan
An Index Scan node uses an index to access specific records matching the predicate. This node then obtains other relevant columns by scanning the heap. This node type supports forward and backward ordering. Heap access is required because an index doesn't store visibility information for columns outside the index. This access method might be slower than others when returning a large number of rows because it doesn't prefetch the heap.
Index Only Scan
An Index Only Scan node occurs when the index completely covers the predicate and any returned columns. This node type relies heavily on the visibility map, which vacuum maintains, to avoid heap access. If the visibility map isn't up to date, some heap access is required to return the correct data.
Join node types
A Join node type is the method that the query planner chooses to combine rows from two or more tables—or other relations—based on a join condition.
Nested Loop Join
A Nested Loop join node steps through one table and looks for a match in another table or subquery. The goal is for each lookup to use an index, but this depends on the volume of accessed data. This access type works well for smaller datasets but not for bulk operations.
Merge Join
A Merge Join node requires sorted data from both tables or from one table and a
subquery. An index access or an explicit ORDER BY
provides the sort. The
sorted data then eliminates unmatched data through comparison. This join
node works well for bulk operations, but if an ORDER BY
provides the sorted
data, then this access method might be slower than others. The sorting
steps in the following example serve as input to the Merge Join.
(
postgres
@
10
.
3
.
1
.
17
:
5432
)
[
postgres
]
>
EXPLAIN
(
ANALYZE
,
VERBOSE
,
COLUMNAR_ENGINE
,
COSTS
,
SETTINGS
,
BUFFERS
,
WAL
)
SELECT
*
FROM
pgbench_accounts
JOIN
pgbench_branches
USING
(
bid
);
QUERY
PLAN
Merge
Join
(
cost
=
848464
.
58
..
923464
.
83
rows
=
5000000
width
=
457
)
(
actual
time
=
1359
.
470
..
2482
.
524
rows
=
5000000
loops
=
1
)
Output
:
pgbench_accounts
.
bid
,
pgbench_accounts
.
aid
,
pgbench_accounts
.
abalance
,
pgbench_accounts
.
filler
,
pgbench_branches
.
bbalance
,
pgbench_branches
.
filler
Inner
Unique
:
true
Merge
Cond
:
(
pgbench_accounts
.
bid
=
pgbench_branches
.
bid
)
Buffers
:
shared
hit
=
81927
read
=
42
,
temp
read
=
65437
written
=
65440
,
ultra
fast
cache
hit
=
42
I
/
O
Timings
:
shared
read
=
0
.
949
,
temp
read
=
77
.
181
write
=
186
.
481
-
>
Sort
(
cost
=
848461
.
67
..
860961
.
67
rows
=
5000000
width
=
97
)
(
actual
time
=
1359
.
427
..
1741
.
924
rows
=
5000000
loops
=
1
)
Output
:
pgbench_accounts
.
bid
,
pgbench_accounts
.
aid
,
pgbench_accounts
.
abalance
,
pgbench_accounts
.
filler
Sort
Key
:
pgbench_accounts
.
bid
Sort
Method
:
external
merge
Disk
:
523496
kB
Buffers
:
shared
hit
=
81926
read
=
42
,
temp
read
=
65437
written
=
65440
,
ultra
fast
cache
hit
=
42
I
/
O
Timings
:
shared
read
=
0
.
949
,
temp
read
=
77
.
181
write
=
186
.
481
-
>
Seq
Scan
on
public
.
pgbench_accounts
(
cost
=
0
.
00
..
131968
.
00
rows
=
5000000
width
=
97
)
(
actual
time
=
0
.
006
..
476
.
386
rows
=
5000000
loops
=
1
)
Output
:
pgbench_accounts
.
bid
,
pgbench_accounts
.
aid
,
pgbench_accounts
.
abalance
,
pgbench_accounts
.
filler
Buffers
:
shared
hit
=
81926
read
=
42
,
ultra
fast
cache
hit
=
42
I
/
O
Timings
:
shared
read
=
0
.
949
Columnar
Check
:
table
is
not
in
the
columnar
store
-
>
Sort
(
cost
=
2
.
91
..
3
.
04
rows
=
50
width
=
364
)
(
actual
time
=
0
.
038
..
0
.
050
rows
=
50
loops
=
1
)
Output
:
pgbench_branches
.
bbalance
,
pgbench_branches
.
filler
,
pgbench_branches
.
bid
Sort
Key
:
pgbench_branches
.
bid
Sort
Method
:
quicksort
Memory
:
27
kB
Buffers
:
shared
hit
=
1
-
>
Seq
Scan
on
public
.
pgbench_branches
(
cost
=
0
.
00
..
1
.
50
rows
=
50
width
=
364
)
(
actual
time
=
0
.
016
..
0
.
022
rows
=
50
loops
=
1
)
Output
:
pgbench_branches
.
bbalance
,
pgbench_branches
.
filler
,
pgbench_branches
.
bid
Buffers
:
shared
hit
=
1
Columnar
Check
:
table
is
too
small
Settings
:
effective_cache_size
=
'19690120kB'
,
enable_hashjoin
=
'off'
,
enable_nestloop
=
'off'
,
max_parallel_workers_per_gather
=
'0'
,
random_page_cost
=
'1.1'
,
work_mem
=
'256MB'
Query
Identifier
:
6650290151587259687
Planning
:
Buffers
:
shared
hit
=
4
Planning
Time
:
0
.
105
ms
Execution
Time
:
2786
.
403
ms
Hash Join
A Hash Join node is a common join type that depends on available memory. This join type is typically slower to start but much faster after it begins. Hash Join builds a hash table for one side of the join, then builds a corresponding hash table for the other side, and compares those entries.
Because hash tables can be
large, make sure that you provide enough work_mem
to support the operation. work_mem
in conjunction with hash_mem_multiplier
determines the total memory available for
building hash tables. If you notice batching in the node, this indicates that there is
insufficient memory to support the entire hash table in memory. The explain plan
also shows the memory that the hash table used. Batching
is how a query
is processed when it exceeds available memory, which forces data to be processed
in smaller chunks or batches, often on disk.
(
postgres
@
10
.
3
.
1
.
17
:
5432
)
[
postgres
]
>
EXPLAIN
(
ANALYZE
,
VERBOSE
,
COLUMNAR_ENGINE
,
COSTS
,
SETTINGS
,
BUFFERS
,
WAL
)
SELECT
ship_month
,
count
(
*
)
FROM
(
select
date_trunc
(
'month'
,
l_shipdate
)
as
ship_month
,
l_partkey
from
lineitem
)
a
GROUP
BY
ship_month
;
QUERY
PLAN
Hash
Join
(
cost
=
5909
.
88
..
5911
.
20
rows
=
25
width
=
58
)
(
actual
time
=
86
.
467
..
86
.
493
rows
=
25
loops
=
1
)
Hash
Cond
:
(
n
.
n_nationkey
=
c
.
c_nationkey
)
-
>
Seq
Scan
on
nation
n
(
cost
=
0
.
00
..
1
.
25
rows
=
25
width
=
30
)
(
actual
time
=
0
.
012
..
0
.
016
rows
=
25
loops
=
1
)
-
>
Hash
(
cost
=
5909
.
56
..
5909
.
56
rows
=
25
width
=
36
)
(
actual
time
=
86
.
447
..
86
.
448
rows
=
25
loops
=
1
)
Buckets
:
1024
Batches
:
1
Memory
Usage
:
10
kB
-
>
Subquery
Scan
on
c
(
cost
=
5909
.
00
..
5909
.
56
rows
=
25
width
=
36
)
(
actual
time
=
86
.
421
..
86
.
433
rows
=
25
loops
=
1
)
-
>
HashAggregate
(
cost
=
5909
.
00
..
5909
.
31
rows
=
25
width
=
36
)
(
actual
time
=
86
.
420
..
86
.
427
rows
=
25
loops
=
1
)
Group
Key
:
customer
.
c_nationkey
Batches
:
1
Memory
Usage
:
32
kB
-
>
Seq
Scan
on
customer
(
cost
=
0
.
00
..
5159
.
00
rows
=
150000
width
=
10
)
(
actual
time
=
0
.
006
..
28
.
228
rows
=
150000
loops
=
1
)
Planning
Time
:
0
.
179
ms
Execution
Time
:
86
.
551
ms
Aggregate node types
Aggregate node types combine multiple input rows
into a single result row, often in conjunction with aggregate functions like COUNT
, SUM
, AVG
, MAX
, or MIN
, or when a GROUP BY
clause is present.
These nodes process multiple input rows to produce single, aggregated results.
GroupAggregate
The GroupAggregate node performs all aggregation operations but requires
sorted data from all input nodes. Because the data is sorted, this node type
requires less memory and returns sorted data. Indexes covering the GROUP BY
clause help speed up this aggregation type. This aggregation type addresses all
PostgreSQL aggregate types, including the following:
-
count(distinct ...) -
array_agg(...) -
order by ...
(
postgres
@
10
.
3
.
1
.
17
:
5432
)
[
postgres
]
>
EXPLAIN
(
ANALYZE
,
VERBOSE
,
COLUMNAR_ENGINE
,
COSTS
,
SETTINGS
,
BUFFERS
,
WAL
)
SELECT
ship_month
,
count
(
*
)
FROM
(
select
date_trunc
(
'month'
,
l_shipdate
)
as
ship_month
,
l_partkey
from
lineitem
)
a
GROUP
BY
ship_month
;
QUERY
PLAN
GroupAggregate
(
cost
=
1085309
.
69
..
1130355
.
32
rows
=
2508
width
=
16
)
(
actual
time
=
4769
.
489
..
8799
.
882
rows
=
83
loops
=
1
)
Group
Key
:
(
date_trunc
(
'month'
::
text
,
l_shipdate
))
-
>
Sort
(
cost
=
1085309
.
69
..
1100314
.
45
rows
=
6001903
width
=
14
)
(
actual
time
=
4763
.
972
..
5441
.
191
rows
=
6001589
loops
=
1
)
Sort
Key
:
(
date_trunc
(
'month'
::
text
,
l_shipdate
))
Sort
Method
:
external
merge
Disk
:
146264
kB
-
>
Seq
Scan
on
lineitem
(
cost
=
0
.
00
..
204436
.
79
rows
=
6001903
width
=
14
)
(
actual
time
=
0
.
065
..
2061
.
266
rows
=
6001589
loops
=
1
)
Planning
Time
:
0
.
114
ms
Execution
Time
:
8827
.
120
ms
HashAggregate
The HashAggregate node performs basic aggregation operations and uses unsorted data from all input nodes. Because the data is unsorted, the node requires more memory and returns unsorted data.
Because hash tables can be
large, make sure that you provide enough work_mem
to support the operation. work_mem
in conjunction with hash_mem_multiplier
determines the total memory available for
building hash tables. If you notice batching in the node, this indicates that there is
insufficient memory to support the entire hash table in memory. The explain plan
also shows the memory that the hash table used.
(
postgres
@
10
.
3
.
1
.
17
:
5432
)
[
postgres
]
>
EXPLAIN
(
ANALYZE
,
VERBOSE
,
COLUMNAR_ENGINE
,
COSTS
,
SETTINGS
,
BUFFERS
,
WAL
)
SELECT
ship_month
,
count
(
*
)
FROM
(
select
date_trunc
(
'month'
,
l_shipdate
)
as
ship_month
,
l_partkey
from
lineitem
)
a
GROUP
BY
ship_month
;
QUERY
PLAN
Hash
Join
(
cost
=
5909
.
88
..
5911
.
20
rows
=
25
width
=
58
)
(
actual
time
=
86
.
467
..
86
.
493
rows
=
25
loops
=
1
)
Hash
Cond
:
(
n
.
n_nationkey
=
c
.
c_nationkey
)
-
>
Seq
Scan
on
nation
n
(
cost
=
0
.
00
..
1
.
25
rows
=
25
width
=
30
)
(
actual
time
=
0
.
012
..
0
.
016
rows
=
25
loops
=
1
)
-
>
Hash
(
cost
=
5909
.
56
..
5909
.
56
rows
=
25
width
=
36
)
(
actual
time
=
86
.
447
..
86
.
448
rows
=
25
loops
=
1
)
Buckets
:
1024
Batches
:
1
Memory
Usage
:
10
kB
-
>
Subquery
Scan
on
c
(
cost
=
5909
.
00
..
5909
.
56
rows
=
25
width
=
36
)
(
actual
time
=
86
.
421
..
86
.
433
rows
=
25
loops
=
1
)
-
>
HashAggregate
(
cost
=
5909
.
00
..
5909
.
31
rows
=
25
width
=
36
)
(
actual
time
=
86
.
420
..
86
.
427
rows
=
25
loops
=
1
)
Group
Key
:
customer
.
c_nationkey
Batches
:
1
Memory
Usage
:
32
kB
-
>
Seq
Scan
on
customer
(
cost
=
0
.
00
..
5159
.
00
rows
=
150000
width
=
10
)
(
actual
time
=
0
.
006
..
28
.
228
rows
=
150000
loops
=
1
)
Planning
Time
:
0
.
179
ms
Execution
Time
:
86
.
551
ms
Parallel node types
Parallel node types facilitate parallel query execution. These nodes work together to distribute work among multiple worker processes and then consolidate the results.
Gather
The Gather node collects and assembles data from worker processes.
Parallel Sequential Scan
Based on the number of workers that the planner determines it can use, the leader assigns blocks to individual workers. Those workers then scan those blocks sequentially for relevant data and pass it back to the Gather node, which combines the results.
Parallel Bitmap Heap Scan
A Parallel Bitmap Heap Scan gathers data like a Parallel Seq Scan, but it emulates the behavior of a Bitmap Heap Scan. The leader process performs the Bitmap Index Scan and builds the bitmap. The leader then assigns portions of that bitmap to workers to perform the Parallel Bitmap Heap Scan. A Gather step is required to assemble data from the worker processes.
Parallel Index Scan
A Parallel Index Scan works like an Index Scan, except that each worker takes turns reading the index. Each worker outputs its data in sorted order for that worker. Once the data passes back to the leader, the system sorts the data a final time to support ordered index scans. Parallel index scans are only supported for B-Tree index types.
Parallel Index Only Scan
A Parallel Index Only Scan works like a Parallel Index Scan. In certain circumstances, the heap doesn't require a visit.
Parallel aggregate node types
A parallel aggregate is a mechanism for speeding up the execution of aggregate functions by distributing the aggregation work across multiple worker processes.
Partial Aggregate
When a parallel sequential or index scan executes with an aggregation, each worker aggregates its data, which results in a partial aggregate.
Finalize Aggregate
The Finalize Aggregate occurs after the partial aggregations pass to the leader. The leader process then finalizes the aggregation. Not all aggregations use parallel worker processes.
Other node types
This section contains information about other node types like Bitmap And/Or, Materialize, and Sort.
Bitmap And/Or
The Bitmap And/Or node combines bitmaps from multiple Bitmap Index Scans.
The combined bitmap then passes to the Bitmap Heap Scan, which removes the need
to run the heap scan more than once. This node has the same work_mem
limitations as the Bitmap Index Scan and might require more work_mem
to
reduce lossiness.
Lossiness means that an index might return more results than necessary, forcing the database to perform an extra check to filter out the incorrect results. The "loss" refers to the index losing some of the detail that's needed to identify the correct rows on its own.
(
postgres
@
10
.
3
.
1
.
17
:
5432
)
[
postgres
]
>
EXPLAIN
(
ANALYZE
,
VERBOSE
,
COLUMNAR_ENGINE
,
COSTS
,
SETTINGS
,
BUFFERS
,
WAL
)
SELECT
*
FROM
public
.
effective_io_concurrency_test
eict
WHERE
id
BETWEEN
10000
AND
100000
OR
product_id
BETWEEN
100
AND
200
;
QUERY
PLAN
Bitmap
Heap
Scan
on
public
.
effective_io_concurrency_test
eict
(
cost
=
10318
.
30
..
488198
.
00
rows
=
619435
width
=
31
)
(
actual
time
=
113
.
696
..
2443
.
712
rows
=
589389
loops
=
1
)
Output
:
id
,
value
,
product_id
,
effective_date
Recheck
Cond
:
(((
eict
.
id
> =
10000
)
AND
(
eict
.
id
< =
100000
))
OR
((
eict
.
product_id
> =
100
)
AND
(
eict
.
product_id
< =
200
)))
Heap
Blocks
:
exact
=
378308
Buffers
:
shared
hit
=
1784
read
=
379535
written
=
4115
I
/
O
Timings
:
shared
read
=
1201
.
096
write
=
37
.
558
-
>
BitmapOr
(
cost
=
10318
.
30
..
10318
.
30
rows
=
619925
width
=
0
)
(
actual
time
=
68
.
895
..
68
.
896
rows
=
0
loops
=
1
)
Buffers
:
shared
hit
=
501
read
=
2510
I
/
O
Timings
:
shared
read
=
11
.
655
-
>
Bitmap
Index
Scan
on
effective_io_concurrency_test_pkey
(
cost
=
0
.
00
..
1807
.
88
rows
=
92951
width
=
0
)
(
actual
time
=
2
.
513
..
2
.
513
rows
=
90001
loops
=
1
)
Index
Cond
:
((
eict
.
id
> =
10000
)
AND
(
eict
.
id
< =
100000
))
Buffers
:
shared
hit
=
496
-
>
Bitmap
Index
Scan
on
effective_io_concurrency_test_groupby
(
cost
=
0
.
00
..
8200
.
71
rows
=
526974
width
=
0
)
(
actual
time
=
66
.
380
..
66
.
380
rows
=
499871
loops
=
1
)
Index
Cond
:
((
eict
.
product_id
> =
100
)
AND
(
eict
.
product_id
< =
200
))
Buffers
:
shared
hit
=
5
read
=
2510
I
/
O
Timings
:
shared
read
=
11
.
655
Settings
:
effective_cache_size
=
'19690120kB'
,
random_page_cost
=
'1.1'
,
work_mem
=
'256MB'
Query
Identifier
:
-
5140071079400709055
Planning
:
Buffers
:
shared
hit
=
36
Planning
Time
:
0
.
133
ms
Execution
Time
:
2477
.
216
ms
AlloyDB
query
id
:
18229116469546507386
AlloyDB
plan
id
:
17462269545806790969
Materialize
Typically seen in Common Table Expressions (CTEs), the Materialize node builds an in-memory tuple store for later reuse. If the CTE isn't materialized and is used multiple times, the system builds the tuple store when needed. Materializing a CTE typically improves performance.
Sort
The Sort step supports any ORDER BY
options in the query. All records
must be visited before the step completes. The Sort node is dependent on work_mem
.
If work_mem
isn't large enough, multiple round trips to disk occur
so that the sort can complete.
Memoize
The Memoize node uses work_mem
and hash_mem_multiplier
to build a
hash table. This hash table caches results for parameterized scans that the
Nested Loop Join node uses. The hash table must fit within memory constraints
in order for the system to use this node. Memoize can significantly speed up Nested
Loops because the system doesn't re-execute the scan for each loop.
Append
The Append node handles operations like UNION
or UNION ALL
. When two
nodes combine data, an Append
node appears.
Limit
The Limit node returns a subset of records specified by the LIMIT
clause. It
sometimes works with the OFFSET
clause. In cases of OFFSET
, the cost to
return the first row might be higher than expected.
CTE Scan
The CTE Scan node uses results from a Common Table Expression (CTE) to join to
another node. Depending on how many times you use a CTE, using the MATERIALIZE
keyword with the CTE instantiation might be beneficial.
Custom Scan
A Custom Scan node is specific to AlloyDB. This node indicates that the node operates on the columnar engine .
(
postgres
@
10
.
3
.
1
.
17
:
5432
)
[
postgres
]
>
EXPLAIN
(
ANALYZE
,
VERBOSE
,
COLUMNAR_ENGINE
)
select
*
from
public
.
index_advisor_test
where
product_id
=
1
;
QUERY
PLAN
Append
(
cost
=
20
.
00
..
27438
.
78
rows
=
1166668
width
=
27
)
(
actual
time
=
0
.
066
..
377
.
029
rows
=
1000290
loops
=
1
)
-
>
Custom
Scan
(
columnar
scan
)
on
public
.
index_advisor_test
(
cost
=
20
.
00
..
27437
.
66
rows
=
1166667
width
=
27
)
(
actual
time
=
0
.
065
..
296
.
904
rows
=
1000290
loops
=
1
)
Output
:
id
,
value
,
product_id
,
effective_date
Filter
:
(
index_advisor_test
.
product_id
=
1
)
Rows
Removed
by
Columnar
Filter
:
98999711
Bytes
fetched
from
storage
cache
:
774835915
Columnar
cache
search
mode
:
native
Swap
-
in
Time
:
92
.
708
ms
-
>
Seq
Scan
on
public
.
index_advisor_test
(
cost
=
0
.
00
..
1
.
11
rows
=
1
width
=
27
)
(
never
executed
)
Output
:
id
,
value
,
product_id
,
effective_date
Filter
:
(
index_advisor_test
.
product_id
=
1
)
Query
Identifier
:
-
4660018746142248761
Planning
Time
:
0
.
217
ms
Execution
Time
:
421
.
114
ms
AlloyDB
query
id
:
13855683355620344431
AlloyDB
plan
id
:
2126918133221480510
This plan output includes the following information:
- Query Filter (predicate): this shows the applied filter, if you use one.
- Rows Removed by Columnar Filter: this indicates the number of rows that the columnar filter removed.
- Bytes fetched from storage cache: this shows the number of bytes retrieved from the storage cache.
- Swap-in Time: this is the time required to swap data from the columnar spill cache (SSD) if the relation doesn't fit into memory.

