A partial index is an index that is built over a subset of table rows that meet some condition. A partial index can ensure data integrity by enforcing constraints on that subset of table rows.

Let's look at an example. Suppose we have to track each successful and failed payment for every order in an e-commerce application. An order may have any number of failed payments but no more than one successful payment. We can model payments with the following table, assuming that status column has success and failure values for successful and failed payments respectively:

CREATE TABLE payments (
  id              SERIAL  NOT NULL,
  status          VARCHAR NOT NULL,
  order_id        INTEGER NOT NULL,
  amount_in_cents INTEGER NOT NULL
);

To allow the table to have no more than one record with success status and any number of records with failure status for each order_id we can create a unique partial index:

CREATE UNIQUE INDEX payments_order_id_idx ON payments (order_id)
WHERE status = 'success';

Let's see how this constraint is applied. We can add two rows to the table with the same order_id and different statuses:

INSERT INTO payments (status, order_id, amount_in_cents) VALUES ('failure', 1, 1000);
INSERT INTO payments (status, order_id, amount_in_cents) VALUES ('success', 1, 1000);

But if we try to add one more row with the same order_id and status equal to success:

INSERT INTO payments (status, order_id, amount_in_cents) VALUES ('success', 1, 1000);

we get an error that says duplicate key value violates unique constraint "payments_order_id_idx". However, we can still add rows with failure status for the same order:

INSERT INTO payments (status, order_id, amount_in_cents) VALUES ('failure', 1, 1000);

Partial indexes can make queries even faster than the same "full" indexes without a condition. I'll be talking about that in the next posts.

Also, check PostgreSQL docs to learn more about partial indexes.