diff --git a/pgFirstAid.sql b/pgFirstAid.sql index 931e1c0..1d741a0 100644 --- a/pgFirstAid.sql +++ b/pgFirstAid.sql @@ -383,7 +383,7 @@ where and n.nspname = pt.schemaname and c.relname = pt.tablename ); --- CRITICAL: Unused indexes consuming significant space +-- CRITICAL: Large indexes never used for reads consuming significant space insert into health_results @@ -392,7 +392,7 @@ where 'Table Health' as category, 'Unused Large Index' as check_name, quote_ident(psi.schemaname) || '.' || quote_ident(psio.indexrelname) as object_name, - 'Large unused index consuming disk space and potentially impacting write performance' as issue_description, + 'Large index never used for reads, consuming disk space and potentially impacting write performance' as issue_description, pg_size_pretty(pg_relation_size(psi.indexrelid)) || ' (0 scans)' as current_value, 'Consider dropping this index if truly unused after monitoring usage patterns. Never drop an index without validating usage!' as recommended_action, 'https://www.postgresql.org/docs/current/sql-dropindex.html' as documentation_link, @@ -401,10 +401,37 @@ from pg_stat_user_indexes psi join pg_statio_user_indexes psio on psi.indexrelid = psio.indexrelid +join pg_index pgi on + pgi.indexrelid = psi.indexrelid where idx_scan = 0 - and pg_relation_size(psi.indexrelid) > 104857600; --- 100MB + and not pgi.indisunique + and not pgi.indisexclusion + and pg_relation_size(psi.indexrelid) > 104857600; -- 100MB +-- MEDIUM: Large unique/constraint indexes never used for reads + insert + into + health_results + select + 'MEDIUM' as severity, + 'Table Health' as category, + 'Unread Large Constraint-Backing Index' as check_name, + quote_ident(psi.schemaname) || '.' || quote_ident(psio.indexrelname) as object_name, + 'Large unique or constraint index never used for reads. It is enforced on every write, so review whether the constraint is needed.' as issue_description, + pg_size_pretty(pg_relation_size(psi.indexrelid)) || ' (0 scans)' as current_value, + 'Confirm the underlying unique or primary-key constraint is still required. Drop the constraint if it is not necessary.' as recommended_action, + 'https://www.postgresql.org/docs/current/ddl-constraints.html' as documentation_link, + 3 as severity_order +from + pg_stat_user_indexes psi +join pg_statio_user_indexes psio on + psi.indexrelid = psio.indexrelid +join pg_index pgi on + pgi.indexrelid = psi.indexrelid +where + idx_scan = 0 + and (pgi.indisunique or pgi.indisexclusion) + and pg_relation_size(psi.indexrelid) > 104857600; -- 100MB -- HIGH: Inactive Replication slots insert into diff --git a/view_pgFirstAid.sql b/view_pgFirstAid.sql index 3791d2d..9b0e2b5 100644 --- a/view_pgFirstAid.sql +++ b/view_pgFirstAid.sql @@ -375,13 +375,13 @@ where and c.relname = pt.tablename ) union all --- CRITICAL: Unused indexes consuming significant space +-- CRITICAL: Large indexes never used for reads consuming significant space select 'CRITICAL' as severity, 'Table Health' as category, 'Unused Large Index' as check_name, quote_ident(psi.schemaname) || '.' || quote_ident(psio.indexrelname) as object_name, - 'Large unused index consuming disk space and potentially impacting write performance' as issue_description, + 'Large index never used for reads, consuming disk space and potentially impacting write performance' as issue_description, pg_size_pretty(pg_relation_size(psi.indexrelid)) || ' (0 scans)' as current_value, 'Consider dropping this index if truly unused after monitoring usage patterns. Never drop an index without validating usage!' as recommended_action, 'https://www.postgresql.org/docs/current/sql-dropindex.html' as documentation_link, @@ -390,10 +390,35 @@ from pg_stat_user_indexes psi join pg_statio_user_indexes psio on psi.indexrelid = psio.indexrelid +join pg_index pgi on + pgi.indexrelid = psi.indexrelid where idx_scan = 0 - and pg_relation_size(psi.indexrelid) > 104857600 - -- 100MB + and not pgi.indisunique + and not pgi.indisexclusion + and pg_relation_size(psi.indexrelid) > 104857600 -- 100MB +union all +-- MEDIUM: Large unique/constraint indexes never used for reads + select + 'MEDIUM' as severity, + 'Table Health' as category, + 'Unread Large Constraint-Backing Index' as check_name, + quote_ident(psi.schemaname) || '.' || quote_ident(psio.indexrelname) as object_name, + 'Large unique or constraint index never used for reads. It is enforced on every write, so review whether the constraint is needed.' as issue_description, + pg_size_pretty(pg_relation_size(psi.indexrelid)) || ' (0 scans)' as current_value, + 'Confirm the underlying unique or primary-key constraint is still required. Drop the constraint if it is not necessary.' as recommended_action, + 'https://www.postgresql.org/docs/current/ddl-constraints.html' as documentation_link, + 3 as severity_order +from + pg_stat_user_indexes psi +join pg_statio_user_indexes psio on + psi.indexrelid = psio.indexrelid +join pg_index pgi on + pgi.indexrelid = psi.indexrelid +where + idx_scan = 0 + and (pgi.indisunique or pgi.indisexclusion) + and pg_relation_size(psi.indexrelid) > 104857600 -- 100MB union all -- HIGH: Inactive Replication slots (with q as ( diff --git a/view_pgFirstAid_managed.sql b/view_pgFirstAid_managed.sql index 7a6373f..5d46afe 100644 --- a/view_pgFirstAid_managed.sql +++ b/view_pgFirstAid_managed.sql @@ -370,13 +370,13 @@ where and c.relname = pt.tablename ) union all --- CRITICAL: Unused indexes consuming significant space +-- CRITICAL: Large indexes never used for reads consuming significant space select 'CRITICAL' as severity, 'Table Health' as category, 'Unused Large Index' as check_name, quote_ident(psi.schemaname) || '.' || quote_ident(psio.indexrelname) as object_name, - 'Large unused index consuming disk space and potentially impacting write performance' as issue_description, + 'Large index never used for reads, consuming disk space and potentially impacting write performance' as issue_description, pg_size_pretty(pg_relation_size(psi.indexrelid)) || ' (0 scans)' as current_value, 'Consider dropping this index if truly unused after monitoring usage patterns. Never drop an index without validating usage!' as recommended_action, 'https://www.postgresql.org/docs/current/sql-dropindex.html' as documentation_link, @@ -385,10 +385,35 @@ from pg_stat_user_indexes psi join pg_statio_user_indexes psio on psi.indexrelid = psio.indexrelid +join pg_index pgi on + pgi.indexrelid = psi.indexrelid where idx_scan = 0 - and pg_relation_size(psi.indexrelid) > 104857600 - -- 100MB + and not pgi.indisunique + and not pgi.indisexclusion + and pg_relation_size(psi.indexrelid) > 104857600 -- 100MB +union all +-- MEDIUM: Large unique/constraint indexes never used for reads + select + 'MEDIUM' as severity, + 'Table Health' as category, + 'Unread Large Constraint-Backing Index' as check_name, + quote_ident(psi.schemaname) || '.' || quote_ident(psio.indexrelname) as object_name, + 'Large unique or constraint index never used for reads. It is enforced on every write, so review whether the constraint is needed.' as issue_description, + pg_size_pretty(pg_relation_size(psi.indexrelid)) || ' (0 scans)' as current_value, + 'Confirm the underlying unique or primary-key constraint is still required. Drop the constraint if it is not necessary.' as recommended_action, + 'https://www.postgresql.org/docs/current/ddl-constraints.html' as documentation_link, + 3 as severity_order +from + pg_stat_user_indexes psi +join pg_statio_user_indexes psio on + psi.indexrelid = psio.indexrelid +join pg_index pgi on + pgi.indexrelid = psi.indexrelid +where + idx_scan = 0 + and (pgi.indisunique or pgi.indisexclusion) + and pg_relation_size(psi.indexrelid) > 104857600 -- 100MB union all -- HIGH: Inactive Replication slots (with q as (