mnemo_cards/mnemo_cards_backend/migrations/003_add_composite_indexes.sql

198 lines
5.9 KiB
MySQL
Raw Normal View History

2026-01-03 13:14:27 +00:00
-- Migration 003: Add composite and covering indexes for performance
-- Date: 2025-12-14
-- Description: Create optimized indexes for frequent queries
-- =====================================================
-- 1. User-Pack relationship indexes
-- =====================================================
-- Composite index for "get user packs" query
CREATE INDEX IF NOT EXISTS idx_user_packs_composite
ON user_packs(user_id, pack_id)
INCLUDE (granted_at, grant_type);
-- Reverse index for "which users have this pack"
CREATE INDEX IF NOT EXISTS idx_user_packs_pack_composite
ON user_packs(pack_id, user_id)
WHERE grant_type != 'admin';
-- =====================================================
-- 2. Card-Pack relationship indexes
-- =====================================================
-- Composite index for "get pack cards ordered"
CREATE INDEX IF NOT EXISTS idx_card_pack_cards_ordered
ON card_pack_cards(pack_id, "order")
INCLUDE (card_id);
-- Index for cards
CREATE INDEX IF NOT EXISTS idx_game_cards_pack
ON game_cards(pack_id)
INCLUDE (original, translation)
WHERE is_deleted = false;
-- =====================================================
-- 3. User subscriptions indexes
-- =====================================================
-- Active subscriptions (most common query)
CREATE INDEX IF NOT EXISTS idx_user_subscriptions_active
ON user_subscriptions(user_id, finish)
WHERE finish > NOW();
-- Index for subscription expiration checks
CREATE INDEX IF NOT EXISTS idx_user_subscriptions_expiring
ON user_subscriptions(finish)
WHERE finish BETWEEN NOW() AND NOW() + INTERVAL '7 days';
-- =====================================================
-- 4. Payments indexes
-- =====================================================
-- Covering index for user payments list
CREATE INDEX IF NOT EXISTS idx_payments_user_covering
ON payments(user_id, date DESC)
INCLUDE (amount, currency, status, payment_system);
-- Index for pending/processing payments (for cron jobs)
CREATE INDEX IF NOT EXISTS idx_payments_pending
ON payments(status, date DESC)
WHERE status IN ('pending', 'processing', 'created');
-- Index for successful payments analytics
CREATE INDEX IF NOT EXISTS idx_payments_succeeded
ON payments(date DESC)
WHERE status = 'succeeded';
-- =====================================================
-- 5. Study sessions indexes
-- =====================================================
-- User sessions ordered by time
CREATE INDEX IF NOT EXISTS idx_study_sessions_user_time
ON study_sessions(user_id, start_time DESC)
INCLUDE (words_learned, tests_completed, accuracy);
-- Active sessions (no end_time yet)
CREATE INDEX IF NOT EXISTS idx_study_sessions_active
ON study_sessions(user_id, session_id)
WHERE end_time IS NULL;
-- =====================================================
-- 6. Authentication indexes
-- =====================================================
-- Covering index for token lookup
CREATE INDEX IF NOT EXISTS idx_tokens_token_covering
ON tokens(token)
INCLUDE (user_id, expires, external_user_id);
-- Valid tokens only
CREATE INDEX IF NOT EXISTS idx_tokens_valid
ON tokens(user_id, expires DESC)
WHERE expires > NOW();
-- Refresh tokens covering index
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_jti_covering
ON refresh_tokens(jti)
INCLUDE (user_id, expires_at, is_blacklisted);
-- Active refresh tokens
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_active
ON refresh_tokens(user_id, expires_at DESC)
WHERE is_blacklisted = false AND expires_at > NOW();
-- =====================================================
-- 7. Users indexes
-- =====================================================
-- Email lookup with common fields
CREATE INDEX IF NOT EXISTS idx_users_email_covering
ON users(email)
INCLUDE (id, name, admin, external_user_id)
WHERE email IS NOT NULL AND is_deleted = false;
-- External user ID lookup
CREATE INDEX IF NOT EXISTS idx_users_external_covering
ON users(external_user_id)
INCLUDE (id, name, email, admin);
-- =====================================================
-- 8. Promo codes indexes
-- =====================================================
-- Code lookup
CREATE INDEX IF NOT EXISTS idx_promo_codes_code_covering
ON promo_codes(code)
INCLUDE (campaign_id, user_id, is_used)
WHERE is_used = false;
-- Campaign promo codes
CREATE INDEX IF NOT EXISTS idx_promo_codes_campaign
ON promo_codes(campaign_id, is_used);
-- =====================================================
-- 9. Tests indexes
-- =====================================================
-- Test pack relation
CREATE INDEX IF NOT EXISTS idx_test_pack_relations_pack
ON test_pack_relations(pack_id)
INCLUDE (test_id);
-- Test questions ordered
CREATE INDEX IF NOT EXISTS idx_test_questions_test
ON test_questions(test_id, "order");
-- =====================================================
-- Analyze tables after creating indexes
-- =====================================================
ANALYZE users;
ANALYZE user_packs;
ANALYZE card_pack_cards;
ANALYZE game_cards;
ANALYZE payments;
ANALYZE user_subscriptions;
ANALYZE study_sessions;
ANALYZE tokens;
ANALYZE refresh_tokens;
ANALYZE promo_codes;
ANALYZE tests;
-- =====================================================
-- Проверка созданных индексов
-- =====================================================
SELECT
schemaname,
tablename,
indexname,
indexdef
FROM pg_indexes
WHERE schemaname = 'public'
AND indexname LIKE 'idx_%composite%'
OR indexname LIKE 'idx_%covering%'
ORDER BY tablename, indexname;
-- =====================================================
-- Оценка размера индексов
-- =====================================================
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
LIMIT 20;
PRINT 'Migration 003 completed successfully!';