This guide shows you how to confirm that a database index actually exists, is structured correctly, and is being used by queries — independent of what the query planner tells you. You’ll learn direct system catalog checks, index usage statistics, and physical verification methods that work across major relational databases. No prior knowledge beyond basic SQL is assumed.
What You’ll Need
- Access to the database with sufficient privileges (typically
SELECTon system catalogs andpg_stat_user_indexesor equivalent). - A SQL client or terminal to run queries.
- Knowledge of the table and index names you want to verify.
Step-by-Step
- Check the system catalog directly. The most reliable way to verify an index exists is to query the database’s metadata tables. For PostgreSQL, run:
SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'your_table';
For MySQL/MariaDB:
SHOW INDEX FROM your_table;
For SQL Server:
SELECT name, type_desc FROM sys.indexes WHERE object_id = OBJECT_ID('your_table');This confirms the index’s name, columns, and type (e.g., B-tree, hash) without relying on the planner.
- Verify the index is actually used. Even if the index exists, the planner may ignore it. Check index usage statistics. PostgreSQL:
SELECT schemaname, relname, indexrelname, idx_scan, idx_tup_read, idx_tup_fetch FROM pg_stat_user_indexes WHERE relname = 'your_table';
A non-zero
idx_scanmeans the index has been used. MySQL:SHOW STATUS LIKE 'Handler_read_first';andHandler_read_key— if these increase after running a query, the index is being used. SQL Server:sys.dm_db_index_usage_statsshows seeks, scans, and lookups. - Run a query with forced index usage. Temporarily disable sequential scans (PostgreSQL:
SET enable_seqscan = off;) or use index hints (MySQL:FORCE INDEX (index_name), SQL Server:WITH (INDEX(index_name))) to see if the query still returns correct results. If it does, the index is valid and usable. - Inspect the index structure physically. Use database-specific commands to see the actual index pages. PostgreSQL:
CREATE EXTENSION pageinspect;thenSELECT * FROM bt_page_stats('index_name', 1);. MySQL:innodb_index_statstable. SQL Server:DBCC SHOWCONTIG(legacy) orsys.dm_db_index_physical_stats. This confirms the index has allocated pages and is not empty or corrupt. - Check for index corruption. Run a consistency check. PostgreSQL:
REINDEX INDEX index_name;(orREINDEX TABLE). MySQL:CHECK TABLE your_table;. SQL Server:DBCC CHECKTABLE('your_table'). If the check reports no errors, the index is physically sound. - Verify index column order and included columns. The index may exist but not match your query’s WHERE clause. Compare the index definition with your query’s predicates. For composite indexes, the leading column must be used for the index to be effective. Use the catalog queries from step 1 to see the column order.
- Test with a known query plan (but don’t rely on it). Use
EXPLAINto see if the planner chooses the index. If it does, that’s a good sign, but the real proof is the statistics and physical checks above. If the planner doesn’t use it, investigate why — often due to low selectivity, data distribution, or outdated statistics.
Common Mistakes
- Assuming the index is used just because it exists. Many developers create an index and never verify usage. Always check
idx_scanor equivalent. - Checking the wrong table schema. In PostgreSQL, if you have multiple schemas, you must specify the schema in the query. Forgetting it can lead to checking the wrong table.
- Ignoring index size. A very large index may be inefficient. Check its size and compare with the table size — if the index is larger than the table, it may be a sign of poor design.
- Not considering partial or expression indexes. These may not be visible in simple catalog queries. Use
pg_indexeswithindexdefto see the full definition. - Relying solely on
EXPLAIN. The planner can be wrong due to stale statistics. Always cross-check with actual usage stats.
Tips
- Run
ANALYZE(PostgreSQL, MySQL) orUPDATE STATISTICS(SQL Server) before checking planner behavior to ensure statistics are current. - Use
pg_stat_user_indexesover time — a single query may not show usage if the index is rarely used. Monitor for a few days. - For MySQL,
SHOW INDEXalso shows cardinality, which indicates how many unique values the index has. Low cardinality means the index may not be helpful. - In SQL Server,
sys.dm_db_index_usage_statsresets on server restart, so check it after a period of activity. - If you’re verifying an index on a large table, be careful with
REINDEXorCHECK TABLE— they can lock the table. Do it during maintenance windows.
Troubleshooting
Index exists but is never used. Check the query’s WHERE clause — if it uses a function on the column (e.g., WHERE UPPER(name) = 'X'), a regular index won’t help. Create a functional index. Also check data distribution — if the column has many duplicates, the planner may prefer a full scan.
Index appears corrupt. Run REINDEX or CHECK TABLE. If that fails, drop and recreate the index. If the table is large, this may take time — plan accordingly.
Index usage stats show zero scans. This could mean the index was just created, or the queries are using a different index. Run a query that should use the index and re-check stats. If still zero, the planner may be choosing a different path — use EXPLAIN to see why.
Catalog query returns no rows. Double-check the table name and schema. In PostgreSQL, use current_schema() to see your active schema. In MySQL, ensure you’re using the correct database with USE dbname;.
Index is there but queries are slow. The index may be fragmented. Rebuild it (PostgreSQL: REINDEX, MySQL: ALTER TABLE ... DROP INDEX ... ADD INDEX, SQL Server: ALTER INDEX ... REBUILD). Also check if the index covers all columns needed — consider adding included columns to avoid table lookups.
Frequently Asked Questions
Q: Can I verify an index without any privileges? No, you need at least read access to system catalogs. Most databases allow any user to see their own tables’ indexes, but some require specific grants.
Q: How often should I check index usage? For production databases, monitor weekly or monthly. If you’re tuning a specific query, check after running it.
Q: What’s the difference between idx_scan and idx_tup_read? idx_scan counts how many times the index was scanned, while idx_tup_read counts how many rows were fetched from the index. A high scan count with low tuple reads might indicate inefficient usage.
Q: Does the planner always use the best index? No, the planner uses statistics and cost estimates. It can be wrong, especially with outdated statistics or complex queries. That’s why direct verification is important.
Q: Can I verify an index on a replica or read-only database? Yes, you can query system catalogs and usage stats on replicas, but note that usage stats may not be replicated (they are local to each instance). Physical checks like REINDEX are not possible on read-only replicas.
Q: Is there a way to see the actual index size? Yes. PostgreSQL: pg_relation_size('index_name'). MySQL: information_schema.tables with DATA_LENGTH for the index. SQL Server: sys.dm_db_partition_stats.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Turn Off Backup Encryption in iTunes?
- Campus Tech Innovation Hubs: What They Are
- Poco C51 Airtel Prepaid Locked Variant India?
- Jenkins vs GitHub Actions: Which CI/CD Fits Your Team?
- Shared VPS Hosting: Is It Better?
- Disk Defrag Pro: What It Does and Whether You Need It