Initial commit - Church Music Database

This commit is contained in:
2026-01-27 18:04:50 -06:00
commit d367261867
336 changed files with 103545 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
-- Database Optimization Script
-- Date: January 4, 2026
-- Purpose: Remove redundant indexes, optimize queries, improve performance
-- ============================================
-- STEP 1: Remove Redundant Indexes
-- ============================================
-- Remove duplicate index on profile_song_keys
-- Keep: uq_profile_song_key (unique constraint)
-- Keep: idx_profile_song_keys (for lookups)
-- Remove: idx_profile_keys (exact duplicate)
DROP INDEX IF EXISTS idx_profile_keys;
-- Remove redundant single-column index on plan_songs
-- Keep: idx_plan_songs_order (composite index: plan_id, order_index)
-- Remove: idx_plan_songs_plan (redundant - composite index handles this)
DROP INDEX IF EXISTS idx_plan_songs_plan;
-- ============================================
-- STEP 2: Optimize Low-Cardinality Indexes
-- ============================================
-- Replace full index on users.active with partial index
-- Most users are active, so only index the exceptions
DROP INDEX IF EXISTS idx_user_active;
CREATE INDEX IF NOT EXISTS idx_user_inactive ON users (id) WHERE active = false;
-- ============================================
-- STEP 3: Add Composite Indexes for Common Queries
-- ============================================
-- Optimize query: "find plans by profile within date range"
CREATE INDEX IF NOT EXISTS idx_plan_profile_date ON plans (profile_id, date)
WHERE profile_id IS NOT NULL;
-- Add index for plan song lookups (if not exists)
CREATE INDEX IF NOT EXISTS idx_plan_songs_song ON plan_songs (song_id);
-- Add index for profile song reverse lookups (if not exists)
CREATE INDEX IF NOT EXISTS idx_profile_songs_song ON profile_songs (song_id);
-- ============================================
-- STEP 4: Verify Index Effectiveness
-- ============================================
-- Show all indexes after cleanup
SELECT
schemaname,
tablename,
indexname,
indexdef
FROM pg_indexes
WHERE schemaname = 'public'
ORDER BY tablename, indexname;
-- Show index sizes
SELECT
schemaname,
tablename,
indexname,
pg_size_pretty(pg_relation_size(indexrelid)) as index_size
FROM pg_stat_user_indexes
WHERE schemaname = 'public'
ORDER BY pg_relation_size(indexrelid) DESC;
-- ============================================
-- STEP 5: Analyze Tables for Query Planner
-- ============================================
-- Update statistics for query planner optimization
ANALYZE users;
ANALYZE profiles;
ANALYZE songs;
ANALYZE plans;
ANALYZE plan_songs;
ANALYZE profile_songs;
ANALYZE profile_song_keys;
ANALYZE biometric_credentials;
-- ============================================
-- VERIFICATION QUERIES
-- ============================================
-- Check for unused indexes (run after a week of production use)
-- SELECT
-- schemaname,
-- tablename,
-- indexname,
-- idx_scan as scans,
-- pg_size_pretty(pg_relation_size(indexrelid)) as size
-- FROM pg_stat_user_indexes
-- WHERE schemaname = 'public'
-- AND idx_scan = 0
-- AND indexrelid IS NOT NULL
-- ORDER BY pg_relation_size(indexrelid) DESC;
-- Check for missing indexes on foreign keys
-- SELECT
-- c.conname AS constraint_name,
-- t.relname AS table_name,
-- ARRAY_AGG(a.attname ORDER BY u.attposition) AS columns
-- FROM pg_constraint c
-- JOIN pg_class t ON c.conrelid = t.oid
-- JOIN pg_namespace n ON t.relnamespace = n.oid
-- JOIN LATERAL UNNEST(c.conkey) WITH ORDINALITY AS u(attnum, attposition) ON TRUE
-- JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = u.attnum
-- WHERE c.contype = 'f'
-- AND n.nspname = 'public'
-- GROUP BY c.conname, t.relname;
COMMIT;