sql Database

Index Optimization Queries

SQL queries to identify missing indexes, unused indexes, duplicate indexes, and index bloat in PostgreSQL databases.

Apex Logic 0 copies
sql
-- PostgreSQL Index Optimization Toolkit

-- 1. Find missing indexes (tables with sequential scans)
SELECT
    schemaname, relname AS table_name,
    seq_scan, seq_tup_read,
    idx_scan, idx_tup_fetch,
    n_tup_ins + n_tup_upd + n_tup_del AS writes,
    pg_size_pretty(pg_relation_size(relid)) AS table_size
FROM pg_stat_user_tables
WHERE seq_scan > 1000
  AND idx_scan < seq_scan / 2
  AND pg_relation_size(relid) > 10 * 1024 * 1024
ORDER BY seq_tup_read DESC
LIMIT 20;

-- 2. Find unused indexes (candidates for removal)
SELECT
    s.schemaname, s.relname AS table_name,
    s.indexrelname AS index_name,
    s.idx_scan AS times_used,
    pg_size_pretty(pg_relation_size(i.indexrelid)) AS index_size,
    i.indisunique AS is_unique
FROM pg_stat_user_indexes s
JOIN pg_index i ON s.indexrelid = i.indexrelid
WHERE s.idx_scan < 50
  AND NOT i.indisunique
  AND NOT i.indisprimary
  AND pg_relation_size(i.indexrelid) > 1024 * 1024
ORDER BY pg_relation_size(i.indexrelid) DESC;

-- 3. Find duplicate indexes
SELECT
    a.indrelid::regclass AS table_name,
    a.indexrelid::regclass AS index_a,
    b.indexrelid::regclass AS index_b,
    pg_size_pretty(pg_relation_size(a.indexrelid)) AS size_a
FROM pg_index a
JOIN pg_index b ON a.indrelid = b.indrelid
    AND a.indexrelid < b.indexrelid
    AND a.indkey::text = b.indkey::text
WHERE a.indrelid::regclass::text NOT LIKE 'pg_%';

-- 4. Index bloat estimation
SELECT
    current_database(), nspname AS schema, tblname AS table,
    idxname AS index, bs*(relpages)::bigint AS real_size,
    bs*(relpages-est_pages)::bigint AS bloat_size,
    ROUND(100 * (relpages-est_pages)::float / relpages, 1) AS bloat_pct
FROM (
    SELECT *, (CASE WHEN relpages > est_pages THEN relpages ELSE est_pages END) AS safe_pages
    FROM (SELECT coalesce(1 + ceil(reltuples / floor((bs-pageopqdata-pagehdr) /
        (4+nulldatahdrwidth)::float)), 0) AS est_pages, bs, nspname, tblname,
        idxname, relpages, reltuples
    FROM (SELECT 8192 AS bs, 24 AS pageopqdata, 8 AS pagehdr,
        CASE WHEN max(coalesce(s.null_frac, 0)) > 0 THEN 2 ELSE 0 END +
        max(coalesce(s.avg_width, 0)) AS nulldatahdrwidth,
        n.nspname, c.relname AS tblname, i.relname AS idxname,
        i.relpages, i.reltuples
    FROM pg_index x
    JOIN pg_class c ON c.oid = x.indrelid
    JOIN pg_class i ON i.oid = x.indexrelid
    JOIN pg_namespace n ON n.oid = c.relnamespace
    JOIN pg_stats s ON s.tablename = c.relname AND s.attname = ANY(
        ARRAY(SELECT a.attname FROM pg_attribute a WHERE a.attrelid = x.indrelid
              AND a.attnum = ANY(x.indkey)))
    WHERE n.nspname = 'public'
    GROUP BY n.nspname, c.relname, i.relname, i.relpages, i.reltuples
    ) AS est ) AS est2
) AS est3
WHERE relpages > 10 AND (relpages - est_pages) > 0
ORDER BY bloat_size DESC LIMIT 20;

Tags

indexing optimization performance dba

Related Snippets

javascript

MongoDB Connection with Retry

bash

MongoDB Backup Script

sql

User Analytics Query

sql

Efficient Pagination Query