All Products
Search
Document Center

PolarDB:Overview

Last Updated:Mar 28, 2026

Global indexes let you index any column of a partitioned table — not just the partition key — and enforce unique constraints across all partitions. This topic covers how global indexes work, how to create them, and what scan types they enable.

How it works

A local index maps one index partition to each table partition. Because its partitioning mirrors the table's partitioning, a local index must include the partition key to enforce a unique constraint.

A global index is a single B-tree index defined across the entire partitioned table. It spans all table partitions, so queries on the indexed column go through one index structure instead of scanning each partition separately. Unlike local indexes, a global index can be created on any column — including non-partition key columns — and it supports unique constraints on those columns.

Global partitioned indexes take this further: the index itself is partitioned, but its partitioning does not need to align with the table's partitioning. This is typically used on partitioned tables with large data volumes where additional query performance on the indexed partition key is valuable.

When to use a global index

Use the following decision guide to choose between a local index and a global index:

  1. If the index columns include the partition key — use a local index. Done.

  2. If the constraint must be unique and the index columns do not include the partition key — use a global index. Done.

  3. If manageability is the priority — prefer a local index.

  4. If the workload is OLTP and query response time matters — use a global index for better point-query performance.

Local index vs. global index

DimensionLocal indexGlobal index
Index structureOne partition per table partitionSingle index spanning all table partitions
Partitioning alignment with tableAlways alignedNot required
Unique constraint on non-partition keyNot supportedSupported
Primary/foreign key on non-partition keyNot supportedSupported
Point-query performance on non-partition keyLowerSignificantly higher
Partition pruningYes (when query includes partition key)Yes (optimizer and executor-level)

Create a global index

Syntax

CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] name ] ON [ ONLY ] table_name [ USING method ]
    ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass [ ( opclass_parameter = value [, ... ] ) ] ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
    [ INCLUDE ( column_name [, ...] ) ]
    [ WITH ( storage_parameter [= value] [, ... ] ) ]
[ GLOBAL/LOCAL/global_partitioned_index ]
    [ TABLESPACE tablespace_name ]
    [ WHERE predicate ]
global_partitioned_index:
    GLOBAL PARTITION BY {RANGE (column_list) (index_partitioning_clause) |
                            HASH (column_list) (hash_partitions_by_quantity)}

Specify GLOBAL to create a global index. Without the keyword, a local index is created by default.

Examples

Create a global index on a non-partition key column:

CREATE UNIQUE INDEX m_city_id_idx_global ON measurement(city_id) GLOBAL;

Create a global partitioned index partitioned by range on the same table:

CREATE UNIQUE INDEX m_peaktemp_idx_global ON measurement(peaktemp) GLOBAL
PARTITION BY RANGE (peaktemp)
(
    PARTITION m_peaktemp_idx_global_p1 VALUES LESS THAN(1000),
    PARTITION m_peaktemp_idx_global_p2 VALUES LESS THAN(5000),
    PARTITION m_peaktemp_idx_global_p3 VALUES LESS THAN(MAXVALUE)
);

Usage notes

  • CONCURRENTLY is supported in CREATE INDEX statements for global indexes.

  • Global indexes can be range- or hash-partitioned, using the same syntax as data table partitioning.

  • Global indexes cannot be list-partitioned.

  • Global indexes can be partitioned on the table partition key.

  • The partitioning of a global index must be based on a left prefix of the index columns.

  • Range-partitioned global indexes support ordered scanning.

  • Hash-partitioned global indexes do not support sequential scans.

  • Global partitioned indexes can enforce unique constraints globally.

  • Global partitioned indexes support partition pruning at both the optimizer and executor level.

  • Global indexes cannot be created on non-partitioned tables or on child partitions of a partitioned table.

  • Global indexes cannot be created based on expressions.

Global index scans

Global indexes support the following scan types:

  • Global index scan

  • Global partitioned index scan

  • Global index-only scan

  • Global partitioned index-only scan

  • Global index bitmap scan

  • Global partitioned index bitmap scan

  • Global index parallel scan

  • Global only index parallel scan

Examples

All examples below use the measurement table and indexes created in the examples above.

Global index scan

EXPLAIN SELECT * FROM measurement WHERE city_id = 5;
                                           QUERY PLAN
------------------------------------------------------------------------------------------------
 Global Index Scan using m_city_id_idx_global on measurement  (cost=0.12..8.14 rows=1 width=20)
   Index Cond: (city_id = 5)
(2 rows)

Global partitioned index scan

EXPLAIN SELECT * FROM measurement WHERE peaktemp = 5;
                                                    QUERY PLAN
------------------------------------------------------------------------------------------------------------------
 Global Partitioned Index Scan(1/3) using m_peaktemp_idx_global on measurement  (cost=0.50..8.52 rows=1 width=20)
   Index Cond: (peaktemp = 5)
(2 rows)

Global index-only scan

EXPLAIN SELECT city_id FROM measurement WHERE city_id = 5;
                                             QUERY PLAN
----------------------------------------------------------------------------------------------------
 Global Index Only Scan using m_city_id_idx_global on measurement  (cost=0.12..8.14 rows=1 width=4)
   Index Cond: (city_id = 5)
(2 rows)

Global partitioned index-only scan

EXPLAIN SELECT peaktemp FROM measurement WHERE peaktemp = 5;
                                                          QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
 Global Partitioned Index Only Scan(1/3) using m_peaktemp_idx_global on measurement  (cost=0.12..8.14 rows=1 width=4)
   Index Cond: (peaktemp = 5)
(2 rows)

Global index bitmap scan

EXPLAIN SELECT * FROM measurement WHERE city_id > 5 AND city_id < 10000;
                                           QUERY PLAN
------------------------------------------------------------------------------------------------
 Append  (cost=1.03..14.47 rows=32 width=20)
   ->  Bitmap Heap Scan on measurement_y2023q1  (cost=1.03..3.58 rows=8 width=20)
         Recheck Cond: ((city_id > 5) AND (city_id < 10000))
         Filter: ((city_id > 5) AND (city_id < 10000))
         ->  Bitmap Global Index Scan on m_city_id_idx_global  (cost=0.00..4.13 rows=1 width=0)
               Index Cond: ((city_id > 5) AND (city_id < 10000))
   ->  Bitmap Heap Scan on measurement_y2023q2  (cost=1.03..3.58 rows=8 width=20)
         Recheck Cond: ((city_id > 5) AND (city_id < 10000))
         Filter: ((city_id > 5) AND (city_id < 10000))
         ->  Bitmap Global Index Scan on m_city_id_idx_global  (cost=0.00..4.13 rows=1 width=0)
               Index Cond: ((city_id > 5) AND (city_id < 10000))
   ->  Bitmap Heap Scan on measurement_y2023q3  (cost=1.03..3.58 rows=8 width=20)
         Recheck Cond: ((city_id > 5) AND (city_id < 10000))
         Filter: ((city_id > 5) AND (city_id < 10000))
         ->  Bitmap Global Index Scan on m_city_id_idx_global  (cost=0.00..4.13 rows=1 width=0)
               Index Cond: ((city_id > 5) AND (city_id < 10000))
   ->  Bitmap Heap Scan on measurement_y2023q4  (cost=1.03..3.58 rows=8 width=20)
         Recheck Cond: ((city_id > 5) AND (city_id < 10000))
         Filter: ((city_id > 5) AND (city_id < 10000))
         ->  Bitmap Global Index Scan on m_city_id_idx_global  (cost=0.00..4.13 rows=1 width=0)
               Index Cond: ((city_id > 5) AND (city_id < 10000))
(21 rows)

Global partitioned index bitmap scan

EXPLAIN SELECT peaktemp FROM measurement WHERE peaktemp > 5 AND peaktemp < 1000;
                                                    QUERY PLAN
------------------------------------------------------------------------------------------------------------------
 Append  (cost=1.03..14.47 rows=32 width=4)
   ->  Bitmap Heap Scan on measurement_y2023q1  (cost=1.03..3.58 rows=8 width=4)
         Recheck Cond: ((peaktemp > 5) AND (peaktemp < 1000))
         Filter: ((peaktemp > 5) AND (peaktemp < 1000))
         ->  Bitmap Global Partitioned Index Scan(1/3) on m_peaktemp_idx_global  (cost=0.00..4.13 rows=1 width=0)
               Index Cond: ((peaktemp > 5) AND (peaktemp < 1000))
   ->  Bitmap Heap Scan on measurement_y2023q2  (cost=1.03..3.58 rows=8 width=4)
         Recheck Cond: ((peaktemp > 5) AND (peaktemp < 1000))
         Filter: ((peaktemp > 5) AND (peaktemp < 1000))
         ->  Bitmap Global Partitioned Index Scan(1/3) on m_peaktemp_idx_global  (cost=0.00..4.13 rows=1 width=0)
               Index Cond: ((peaktemp > 5) AND (peaktemp < 1000))
   ->  Bitmap Heap Scan on measurement_y2023q3  (cost=1.03..3.58 rows=8 width=4)
         Recheck Cond: ((peaktemp > 5) AND (peaktemp < 1000))
         Filter: ((peaktemp > 5) AND (peaktemp < 1000))
         ->  Bitmap Global Partitioned Index Scan(1/3) on m_peaktemp_idx_global  (cost=0.00..4.13 rows=1 width=0)
               Index Cond: ((peaktemp > 5) AND (peaktemp < 1000))
   ->  Bitmap Heap Scan on measurement_y2023q4  (cost=1.03..3.58 rows=8 width=4)
         Recheck Cond: ((peaktemp > 5) AND (peaktemp < 1000))
         Filter: ((peaktemp > 5) AND (peaktemp < 1000))
         ->  Bitmap Global Partitioned Index Scan(1/3) on m_peaktemp_idx_global  (cost=0.00..4.13 rows=1 width=0)
               Index Cond: ((peaktemp > 5) AND (peaktemp < 1000))
(21 rows)

Primary keys and foreign keys on non-partition key columns

In standard PostgreSQL, a unique constraint on a partitioned table requires the partition key to be part of the constrained columns. This means primary keys and foreign keys on non-partition key columns are not possible without global indexes.

PolarDB for PostgreSQL (Compatible with Oracle) removes this restriction. Any column of a partitioned table can be a primary key or a foreign key, because global indexes can enforce uniqueness on any column.

When a primary key is defined at table creation time, the system automatically selects the index type:

  • If the primary key columns include the partition key — local unique indexes are created.

  • If the primary key columns do not include the partition key — a global unique index is created.

The same rule applies to foreign keys.

Examples

Primary key on the partition key (local index created)

CREATE TABLE pk_rel(a INT PRIMARY KEY, b INT, c INT, d INT) PARTITION BY RANGE (a);
               Table "public.pk_rel"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 a      | integer |           | not null |
 b      | integer |           |          |
 c      | integer |           |          |
 d      | integer |           |          |
Partition key: RANGE (a)
Indexes:
    "pk_rel_pkey" PRIMARY KEY, btree (a)
Number of partitions: 0

Primary key on a non-partition key column (global index created)

CREATE TABLE pk_rel(a INT, b INT PRIMARY KEY, c INT, d INT) PARTITION BY RANGE (a);
               Table "public.pk_rel"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 a      | integer |           |          |
 b      | integer |           | not null |
 c      | integer |           |          |
 d      | integer |           |          |
Partition key: RANGE (a)
Indexes:
    "pk_rel_pkey" PRIMARY KEY, btree (b) GLOBAL
Number of partitions: 0
Note

To explicitly set an existing index as the primary key, use ALTER TABLE xxx ADD PRIMARY KEY USING INDEX xxx.

Foreign key referencing a non-partition key column

CREATE TABLE fk_rel(a INT, b INT REFERENCES pk_rel(b), c INT, d INT) PARTITION BY RANGE (a);
               Table "public.fk_rel"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 a      | integer |           |          |
 b      | integer |           |          |
 c      | integer |           |          |
 d      | integer |           |          |
Partition key: RANGE (a)
Foreign-key constraints:
    "fk_rel_b_fkey" FOREIGN KEY (b) REFERENCES pk_rel(b)
Number of partitions: 0

Limitations and management considerations

Global indexes provide query performance advantages but require careful consideration before use:

  • No expression-based global indexes: Global indexes cannot be created on expressions.

  • Non-partitioned tables: Global indexes cannot be created on non-partitioned tables or on child partitions of a partitioned table.

  • No list partitioning: Global indexes cannot be list-partitioned.

  • Left prefix requirement: The partitioning of a global index must be based on a left prefix of the index columns.

  • No sequential scan on hash-partitioned global indexes: Hash-partitioned global indexes do not support sequential scans.

Performance

The following benchmark data compares point-query and Data Manipulation Language (DML) performance across three table configurations. The test uses pgbench to generate 80,000 rows, then runs queries against a partitioned table (with local indexes or a global index) and a non-partitioned table.

Note

Transactions per second (TPS) results vary by hardware environment. These numbers are intended for relative comparison only.

Point query performance on non-partition keys

ConfigurationWithout prepared statementWith prepared statement
1 concurrent32 concurrent64 concurrent1 concurrent32 concurrent64 concurrent
Non-partitioned table27,732494,433430,84853,935985,880886,882
Partitioned table with local indexes3674,1553,6888568,7426,790
Partitioned table with a global index19,006308,128262,94145,090820,924731,557

TPC-B performance on non-partition keys

Both point queries and DML are included.

ConfigurationWithout prepared statementWith prepared statement
1 concurrent32 concurrent64 concurrent1 concurrent32 concurrent64 concurrent
Non-partitioned table1,11551,02560,4094,82290,312100,802
Partitioned table with local indexes2712,9032,5245505,2764,237
Partitioned table with a global index3,45336,32039,9414,33469,04075,232

Global indexes significantly improve performance for point queries and DML on non-partition key columns. The performance improvement is most pronounced in high-concurrency scenarios: a partitioned table with a global index reaches roughly 74x the TPS of one with local indexes for point queries at 32 concurrent connections.

The underlying reason is index probe efficiency. A local index on a non-partition key must scan all partition indexes to locate matching rows. A global index resolves the query with a single index probe, eliminating the per-partition overhead.