SELECT indrelid::regclass AS TableName
,array_agg(indexrelid::regclass) AS Indexes
FROM pg_index
GROUPBY indrelid
,indkey
HAVINGCOUNT(*) >1;
Finding Unused Indexes
WITH table_scans as (
SELECT relid,
tables.idx_scan + tables.seq_scan as all_scans,
( tables.n_tup_ins + tables.n_tup_upd + tables.n_tup_del ) as writes,
pg_relation_size(relid) as table_size
FROM pg_stat_user_tables as tables
),
all_writes as (
SELECTsum(writes) as total_writes
FROM table_scans
),
indexes as (
SELECT idx_stat.relid, idx_stat.indexrelid,
idx_stat.schemaname, idx_stat.relname as tablename,
idx_stat.indexrelname as indexname,
idx_stat.idx_scan,
pg_relation_size(idx_stat.indexrelid) as index_bytes,
indexdef ~*'USING btree'AS idx_is_btree
FROM pg_stat_user_indexes as idx_stat
JOIN pg_index
USING (indexrelid)
JOIN pg_indexes as indexes
ON idx_stat.schemaname = indexes.schemaname
AND idx_stat.relname = indexes.tablename
AND idx_stat.indexrelname = indexes.indexname
WHERE pg_index.indisunique =FALSE),
index_ratios AS (
SELECT schemaname, tablename, indexname,
idx_scan, all_scans,
round(( CASEWHEN all_scans =0THEN0.0::NUMERICELSE idx_scan::NUMERIC/all_scans *100END),2) as index_scan_pct,
writes,
round((CASEWHEN writes =0THEN idx_scan::NUMERICELSE idx_scan::NUMERIC/writes END),2)
as scans_per_write,
pg_size_pretty(index_bytes) as index_size,
pg_size_pretty(table_size) as table_size,
idx_is_btree, index_bytes
FROM indexes
JOIN table_scans
USING (relid)
),
index_groups AS (
SELECT'Never Used Indexes'as reason, *, 1as grp
FROM index_ratios
WHERE idx_scan =0and idx_is_btree
UNIONALLSELECT'Low Scans, High Writes'as reason, *, 2as grp
FROM index_ratios
WHERE scans_per_write <=1and index_scan_pct <10and idx_scan >0and writes >100and idx_is_btree
UNIONALLSELECT'Seldom Used Large Indexes'as reason, *, 3as grp
FROM index_ratios
WHERE index_scan_pct <5and scans_per_write >1and idx_scan >0and idx_is_btree
and index_bytes >100000000UNIONALLSELECT'High-Write Large Non-Btree'as reason, index_ratios.*, 4as grp
FROM index_ratios, all_writes
WHERE ( writes::NUMERIC/ ( total_writes +1 ) ) >0.02ANDNOT idx_is_btree
AND index_bytes >100000000ORDERBY grp, index_bytes DESC )
SELECT reason, schemaname, tablename, indexname,
index_scan_pct, scans_per_write, index_size, table_size
FROM index_groups;