Initial commit - Church Music Database
This commit is contained in:
112
legacy-site/backend/optimize_database.sql
Normal file
112
legacy-site/backend/optimize_database.sql
Normal 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;
|
||||
Reference in New Issue
Block a user