Postgres indexes
Tags: tutorial, postgres, webdev
Published: 2021-06-21 11:32:10

---
title: Postgres indexes
published: true
description: Postgres indexes
tags: #tutorial,#postgres,#webdev
cover_image: https://miro.medium.com/max/2625/1*115cqXTggxGeZq5m-yFnrw.png
---

## Table of Contents

1. [How To Create An Index](#how-to-create-a-index)
1. [Removing an Index](#remove-an-index)
2. [Identify If A Query Uses An Index](#identify-if-a-query-uses-an-index)
3. [List All Indexes In A Table](#list-all-indexes-in-a-table)
4. [Index Methods](#index-methods)
  1. [Generalized Inverted Indexes](#generalized-inverted-indexes(GIN))
  2. [Generalized Search Tree](#generalized-serch-tree(Gist))
  3. [Hash Index](#hash-index)
  4. [Btree Index](#btree-index)
9. [Indexing Techniques](#indexing-techniques)
  1. [Expression Indexes](#expression-indexes)
  2. [Partial Indexes](#partial-indexes)
  3. [Unique Indexes](#unique-indexes)
  4. [When It Makes Sense To Use Indexes?](#when-it-makes-sense-to-use-indexes?)
10. [Who decides when to use an index or not?] (#who-decides-when-to-use-an-index-or-not?)
11. [You Need A Lot Of Data To Test Indexes](#you-need-a-lot-of-data-to-test-indexes)
12. [Creating Indexes Locks Your DB](#creating-indexes-locks-your-db)
13. [Reindexing](#reindexing)


Indexing is a technique to make queries run faster, different indexes are suitable for different queries, the main differences are between an exact match, ranges and a fulltext, in a nutshell if your query has an exact match or a range you should use the default method, otherwise use gin.

most of the information from this post came from [here](https://devcenter.heroku.com/articles/postgresql-indexes), have a read if you want to dig deeper.

<a name="how-to-create-a-index"></a>
## How To Create An Index


```SQL
CREATE INDEX [UNIQUE] INDEX_NAME ON TABLE_NAME [USING METHOD]
(
  COLUMN_NAME [ASC | DESC] [NULL { FIRST | LAST}]
)
```
e.g:
```SQL
CREATE INDEX idx_name ON users(name)
```
<a name="remove-an-index"></a>
## Removing An Index
  

```SQL
DROP INDEX  [ CONCURRENTLY]
[ IF EXISTS ]  index_name 
[ CASCADE | RESTRICT ];
```

<a name="identify-if-a-query-uses-an-index"></a>
## Identify If A Query Uses An Index

You can use [EXPLAIN](https://www.postgresql.org/docs/9.1/sql-explain.html) to identify if a query is using an specific index

```SQL
EXPLAIN SELECT * FROM BOOKS WHERE BOOK = "MY BOOK";
```

if your query is using the index something like "Index Scan <name of the index>"
will appear e.g:

## Index Is Not Being Used


```
                        QUERY PLAN
----------------------------------------------------------
 Seq Scan on cards  (cost=0.00..62.50 rows=828 width=242)
```

## Index Is Being Used

```
                               QUERY PLAN
------------------------------------------------------------------------
 Index Scan using my_index on cards  (cost=0.28..8.29 rows=1 width=242)
   Index Cond: ((title)::text = 'a'::text)
```

my_index is the name of the index, cards is the name of the table and 
title is the name of the column

<a name="list-all-indexes-in-a-table"></a>
## List All Indexes In A Table

The following will list all indexes related to your tables, you can also 
filter indexes for a specific table or field
```SQL
select tablename, indexname from pg_indexes where schemaname = 'public';
```
<a name="index-methods"></a>
## Index Methods

The following are the most common index methods.

<a name="generalized-inverted-indexes"></a>
## Generalized Inverted Indexes(GIN)

Good for full-text search, maps multiple values to a column

<a name="generalized-serch-tree(gist)"></a>
## Generalized Search Tree(Gist)

Allows for balanced btree, good for full-text search, used for geometric data types(poligons points etc..)
are usefull for operations besides comparison and range.

<a name="hash-index"></a>
## Hash Index

Not worth the complexity and work to manage it, coudn't find any common example that couldn't be done with the other methods in an easier way with better results, you can try to convince me otherwise in the comments if you want.

<a name="btree-index"></a>
## Btree Index

Doesn't work with like queries, works pretty well with caching, it's the default index, if you don't pass the method option when creating the index it will be created with this method.

These are all the most common indexes, now let's talk about some techniques when creating indexes

<a name="expression-indexes"></a>
## Expression Indexes

expression indexes are usefull when you need to convert the data before searching it.

```SQL
CREATE INDEX USERS_LOWER_EMAIL ON USERS(LOWER(EMAIL));
```
the above index will be used in searches like
```SQL
WHERE lower(email) = '<lowercased-email>'
```

```
CREATE INDEX articles_day ON articles ( date(published_at) )
```
the above can will be used for
``` 
WHERE date(articles.published_at) = date('2011-03-07')
```

<a name="partial-indexes"></a>
## Partial Indexes

partial index is an index applied to just part of the data, the following is a usefull partial index

```SQL
CREATE INDEX articles_flagged_created_at_index ON articles(created_at) WHERE flagged IS TRUE;
```
<a name="unique-indexes"></a>
## Unique Indexes

```SQL
create unique <index_name> .....
```
Improves query performance, if you try to create a uniq index in a column that has rows with the same value the index creation will fail.
if you try to insert values that already exist in a column with a uniq index a error occurs
```
ERROR:  duplicate key value violates unique constraint "uniq_index"
DETAIL:  Key (title)=(title 1) already exists.
```
<a name="when-it-makes-sense-to-use-indexes?"></a>
## When it makes sense to use indexes?

The general rule is, the bigger your table is the better
index only makes sense when the cost of reading data from the index(hitting the disc) and then reading data from the table is lower than a table scan, so in general big tables 
are better for indexes because you don't need to scan the whole table and the cost of reading two different values in two different tables isn't very high comparatively with the full table scan, when in doubt just create a test case and test it or better yet, always test it first.

<a name="what-decides-when-to-use-an-index-or-not?"></a>
## What decides when to use an index or not?

The query planner decides to use an index or not, just because an index exists and your query
matches the initial criteria for using that index, doesn't mean that the index will be used.
the query planner decides when to use the index or not based on a multitude of factors, the biggest
one is how much data the query will hit, if it's a lot it's more likely that the query planner will use the index
indexes make more sense the bigger the table is.

<a name="you-need-a-lot-of-data-to-test-indexes-properly"></a>
## You Need A Lot Of Data To Test Indexes Properly

A good general rule is to test as close to production as you can. the query planer might decide not to use your index if you test it locally in small sets of data, which will make you waste a lot of time benchmarking with non sensical data, trust me I've learned the hard way.
so you should create a lot of data to test your indexes, the general rule is to test as close to production as possible, also don't forget to activate /timing when you're testing your queries in psql.

<a name="creating-indexes-locks-your-db"></a>
## Creating Indexes Locks Your DB

If it's a big table your db can be locked for hours, use create index concurrently to mitigate the problem

<a name="reindexing"></a>
## Reindexing

after sometime your index will not be optimized anymore, that's when you need to reindex it
a good reindex technique is to create another index with a different name and then droping the old one.

and that's pretty much all the basic information that you need to start creating indexes, go create some indexes and make your queries run faster!