Index names in PostgreSQL
- Index names are unique across a single database schema.
- Index names cannot be the same as any other index, (foreign) table, (materialized) view, sequence or user-defined composite type in the same schema.
- Two tables in the same schema cannot have an index of the same name. (Follows logically.)
If you do not care about the name of the index, have Postgres auto-name it:
CREATE INDEX ON tbl1 (col1);
is (almost) the same as:
CREATE INDEX tbl1_col1_idx ON tbl1 USING btree (col1);
Except that Postgres will avoid a naming collisions and automatically pick the next free name:
tbl1_col1_idx
tbl1_col1_idx2
tbl1_col1_idx3
...
Just try it. But, obviously, you would not want to create multiple redundant indexes. So it wouldn't be a good idea to just blindly create a new one.
Test for existence
Postgres 9.5 or newer
Now available:
CREATE INDEX IF NOT EXISTS ...
Also works for CREATE INDEX CONCURRENTLY IF NOT EXISTS.
However, the manual warns:
Note that there is no guarantee that the existing index is anything like the one that would have been created.
It's a plain check for the object name. (Applies to variants for older versions below, too.)
To find existing indexes on the same table for the same column(s):
SELECT pg_get_indexdef(indexrelid)
FROM pg_index
WHERE indrelid = 'public.big'::regclass
AND (indkey::int2[])[:] = ARRAY (
SELECT attnum
FROM unnest('{usr_id, created_at}'::text[]) WITH ORDINALITY i(attname, ord)
JOIN (
SELECT attname, attnum
FROM pg_attribute
WHERE attrelid = 'public.big'::regclass
) a USING (attname)
ORDER BY ord
);
Restrictions:
- Only works for columns, not other index expressions.
- Also reports partial indexes (with
WHEREclause) and covering indexes (withINCLUDEclause). - Reports any type of index, not just B-tree indexes.
Study the results (if any) before proceeding, or refine the query to your needs ...
Further reading:
- Find tables with multiple indexes on same column
- Normalize array subscripts for 1-dimensional array so they start with 1
Postgres 9.4
You can use the new function to_regclass() to check without throwing an exception:
DO
$$
BEGIN
IF to_regclass('myschema.mytable_mycolumn_idx') IS NULL THEN
CREATE INDEX mytable_mycolumn_idx ON myschema.mytable (mycolumn);
END IF;
END
$$;
Returns NULL if an index (or another object) of that name does not exist. See:
- How to check if a table exists in a given schema
This doesn't work for CREATE INDEX CONCURRENTLY, since that variant cannot be wrapped in an outer transaction. See comment by @Gregory below.
Postgres 9.3 or older
Cast the schema-qualified name to regclass:
SELECT 'myschema.myname'::regclass;
If it throws an exception, the name is free.
Or, to test the same without throwing an exception, use a DO statement:
DO
$$
BEGIN
IF NOT EXISTS (
SELECT
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'mytable_mycolumn_idx'
AND n.nspname = 'myschema'
) THEN
CREATE INDEX mytable_mycolumn_idx ON myschema.mytable (mycolumn);
END IF;
END
$$;
The DO statement was introduced with Postgres 9.0. In earlier versions you have to create a function to do the same.
Details about pg_class in the manual.
Basics about indexes in the manual.
Index names in PostgreSQL
- Index names are unique across a single database schema.
- Index names cannot be the same as any other index, (foreign) table, (materialized) view, sequence or user-defined composite type in the same schema.
- Two tables in the same schema cannot have an index of the same name. (Follows logically.)
If you do not care about the name of the index, have Postgres auto-name it:
CREATE INDEX ON tbl1 (col1);
is (almost) the same as:
CREATE INDEX tbl1_col1_idx ON tbl1 USING btree (col1);
Except that Postgres will avoid a naming collisions and automatically pick the next free name:
tbl1_col1_idx
tbl1_col1_idx2
tbl1_col1_idx3
...
Just try it. But, obviously, you would not want to create multiple redundant indexes. So it wouldn't be a good idea to just blindly create a new one.
Test for existence
Postgres 9.5 or newer
Now available:
CREATE INDEX IF NOT EXISTS ...
Also works for CREATE INDEX CONCURRENTLY IF NOT EXISTS.
However, the manual warns:
Note that there is no guarantee that the existing index is anything like the one that would have been created.
It's a plain check for the object name. (Applies to variants for older versions below, too.)
To find existing indexes on the same table for the same column(s):
SELECT pg_get_indexdef(indexrelid)
FROM pg_index
WHERE indrelid = 'public.big'::regclass
AND (indkey::int2[])[:] = ARRAY (
SELECT attnum
FROM unnest('{usr_id, created_at}'::text[]) WITH ORDINALITY i(attname, ord)
JOIN (
SELECT attname, attnum
FROM pg_attribute
WHERE attrelid = 'public.big'::regclass
) a USING (attname)
ORDER BY ord
);
Restrictions:
- Only works for columns, not other index expressions.
- Also reports partial indexes (with
WHEREclause) and covering indexes (withINCLUDEclause). - Reports any type of index, not just B-tree indexes.
Study the results (if any) before proceeding, or refine the query to your needs ...
Further reading:
- Find tables with multiple indexes on same column
- Normalize array subscripts for 1-dimensional array so they start with 1
Postgres 9.4
You can use the new function to_regclass() to check without throwing an exception:
DO
$$
BEGIN
IF to_regclass('myschema.mytable_mycolumn_idx') IS NULL THEN
CREATE INDEX mytable_mycolumn_idx ON myschema.mytable (mycolumn);
END IF;
END
$$;
Returns NULL if an index (or another object) of that name does not exist. See:
- How to check if a table exists in a given schema
This doesn't work for CREATE INDEX CONCURRENTLY, since that variant cannot be wrapped in an outer transaction. See comment by @Gregory below.
Postgres 9.3 or older
Cast the schema-qualified name to regclass:
SELECT 'myschema.myname'::regclass;
If it throws an exception, the name is free.
Or, to test the same without throwing an exception, use a DO statement:
DO
$$
BEGIN
IF NOT EXISTS (
SELECT
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'mytable_mycolumn_idx'
AND n.nspname = 'myschema'
) THEN
CREATE INDEX mytable_mycolumn_idx ON myschema.mytable (mycolumn);
END IF;
END
$$;
The DO statement was introduced with Postgres 9.0. In earlier versions you have to create a function to do the same.
Details about pg_class in the manual.
Basics about indexes in the manual.
It will be available in 9.5. Here is actual git commit https://github.com/postgres/postgres/commit/08309aaf74ee879699165ec8a2d53e56f2d2e947
Discussion on pg hackers http://postgresql.nabble.com/CREATE-IF-NOT-EXISTS-INDEX-td5821173.html
postgresql - why would CREATE INDEX IF NOT EXISTS timeout, with postgres? - Stack Overflow
Don't rely on IF NOT EXISTS for concurrent index creation in PostgreSQL
Missing support for CREATE INDEX CONCURRENTLY IF NOT EXISTS - ORM - Django Forum
CREATE INDEX IF NOT EXISTS throws an error
Hello experts,
We have a few tables having size ~5TB and are partitioned on a timestamp column. They have ~90 partitions in them and are storing 90 days of data. We want to create a couple of indexes on those tables. They are getting the incoming transactions(mainly inserts) 24/7 , which are mostly happening on the current day/live partition. Its RDS postgres version 15.4. So in this situation
Should we go with below i.e one time create index command on the table..
CREATE INDEX CONCURRENTLY idx1 ON tab(column_name);
Or
create index on individual partitions from different sessions, say for example create indexes on 30 partitions each from three different sessions so as to finish all the 90 partitions faster?
CREATE INDEX CONCURRENTLY idx1 ON tab_part1(column_name);
CREATE INDEX CONCURRENTLY idx1 ON tab_part2(column_name);..........
Basically I have three questions:
1)If we can do this index creation activity online without impacting the incoming transactions or do we have to take down time for this activity?
2)If we can't do it online then , what is the fastest method to do this index creation activity ?
3)Should we change the DB parameters in a certain way to make the process faster? We have currently set below parameters
max_parallel_workers-16
max_parallel_maintenance_workers-2
maintenance_work_mem- 4GB