From fa67494ec76b085993b5b5bfd3d8d5264dd6a96c Mon Sep 17 00:00:00 2001 From: Adrian Flores Cortes Date: Tue, 3 Feb 2026 17:18:51 -0600 Subject: [PATCH] feat(ddl): Add FK indexes for 32 missing foreign keys Creates 99-additional-fk-indexes.sql with indexes for all FKs that were identified without dedicated index coverage. Organized by priority: - HIGH: billing (3), notifications (1), sales (2), commissions (1) - MEDIUM: audit (2), storage (1), webhooks (1), whatsapp (1) - LOW: created_by/approved_by fields across multiple schemas References: TASK-2026-02-03-REMEDIACION-BD (P1) Based on: INFORME-INTEGRIDAD-REFERENCIAL.md analysis Co-Authored-By: Claude Opus 4.5 --- ddl/99-additional-fk-indexes.sql | 220 +++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 ddl/99-additional-fk-indexes.sql diff --git a/ddl/99-additional-fk-indexes.sql b/ddl/99-additional-fk-indexes.sql new file mode 100644 index 0000000..247837e --- /dev/null +++ b/ddl/99-additional-fk-indexes.sql @@ -0,0 +1,220 @@ +-- ============================================================================= +-- INDICES ADICIONALES PARA FOREIGN KEYS +-- Proyecto: template-saas +-- Generado por: TASK-2026-02-03-REMEDIACION-BD (P1) +-- Fecha: 2026-02-03 +-- Descripcion: Indices para las 32 FKs que carecian de indice dedicado +-- Referencia: orchestration/tareas/TASK-2026-02-03-VALIDACION-INTEGRAL-MODELADO-BD/ +-- entregables/INFORME-INTEGRIDAD-REFERENCIAL.md +-- ============================================================================= + +-- ============================================================================= +-- PRIORIDAD ALTA: Tablas frecuentemente consultadas +-- ============================================================================= + +-- ----------------------------------------------------------------------------- +-- Schema: billing +-- ----------------------------------------------------------------------------- + +-- FK: billing.subscriptions.plan_id -> plans.plans(id) +CREATE INDEX IF NOT EXISTS idx_subscriptions_plan +ON billing.subscriptions(plan_id); + +-- FK: billing.invoices.subscription_id -> billing.subscriptions(id) +CREATE INDEX IF NOT EXISTS idx_invoices_subscription +ON billing.invoices(subscription_id) WHERE subscription_id IS NOT NULL; + +-- FK: billing.payments.invoice_id -> billing.invoices(id) +CREATE INDEX IF NOT EXISTS idx_payments_invoice +ON billing.payments(invoice_id) WHERE invoice_id IS NOT NULL; + +-- ----------------------------------------------------------------------------- +-- Schema: notifications +-- ----------------------------------------------------------------------------- + +-- FK: notifications.notifications.template_id -> notifications.templates(id) +CREATE INDEX IF NOT EXISTS idx_notifications_template +ON notifications.notifications(template_id) WHERE template_id IS NOT NULL; + +-- ----------------------------------------------------------------------------- +-- Schema: sales +-- ----------------------------------------------------------------------------- + +-- FK: sales.leads.created_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_leads_created_by +ON sales.leads(created_by) WHERE created_by IS NOT NULL; + +-- FK: sales.opportunities.created_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_opportunities_created_by +ON sales.opportunities(created_by) WHERE created_by IS NOT NULL; + +-- ----------------------------------------------------------------------------- +-- Schema: commissions +-- ----------------------------------------------------------------------------- + +-- FK: commissions.entries.assignment_id -> commissions.assignments(id) +CREATE INDEX IF NOT EXISTS idx_entries_assignment +ON commissions.entries(assignment_id) WHERE assignment_id IS NOT NULL; + +-- ============================================================================= +-- PRIORIDAD MEDIA: Tablas de auditoria/historial +-- ============================================================================= + +-- ----------------------------------------------------------------------------- +-- Schema: audit +-- ----------------------------------------------------------------------------- + +-- FK: audit.audit_logs.user_id -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_audit_logs_user +ON audit.audit_logs(user_id) WHERE user_id IS NOT NULL; + +-- FK: audit.data_changes.user_id -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_data_changes_user +ON audit.data_changes(user_id) WHERE user_id IS NOT NULL; + +-- ----------------------------------------------------------------------------- +-- Schema: storage +-- ----------------------------------------------------------------------------- + +-- FK: storage.files.uploaded_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_files_uploaded_by +ON storage.files(uploaded_by) WHERE uploaded_by IS NOT NULL; + +-- ----------------------------------------------------------------------------- +-- Schema: webhooks +-- ----------------------------------------------------------------------------- + +-- FK: webhooks.webhooks.created_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_webhooks_created_by +ON webhooks.webhooks(created_by) WHERE created_by IS NOT NULL; + +-- ----------------------------------------------------------------------------- +-- Schema: whatsapp +-- ----------------------------------------------------------------------------- + +-- FK: whatsapp.messages.user_id -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_messages_user +ON whatsapp.messages(user_id) WHERE user_id IS NOT NULL; + +-- ============================================================================= +-- PRIORIDAD BAJA: Campos de auditoria (created_by/approved_by/closed_by/etc) +-- ============================================================================= + +-- ----------------------------------------------------------------------------- +-- Schema: sales +-- ----------------------------------------------------------------------------- + +-- FK: sales.activities.created_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_activities_created_by +ON sales.activities(created_by) WHERE created_by IS NOT NULL; + +-- ----------------------------------------------------------------------------- +-- Schema: portfolio +-- ----------------------------------------------------------------------------- + +-- FK: portfolio.categories.created_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_categories_created_by +ON portfolio.categories(created_by) WHERE created_by IS NOT NULL; + +-- FK: portfolio.products.created_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_products_created_by +ON portfolio.products(created_by) WHERE created_by IS NOT NULL; + +-- ----------------------------------------------------------------------------- +-- Schema: commissions +-- ----------------------------------------------------------------------------- + +-- FK: commissions.schemes.created_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_schemes_created_by +ON commissions.schemes(created_by) WHERE created_by IS NOT NULL; + +-- FK: commissions.assignments.created_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_assignments_created_by +ON commissions.assignments(created_by) WHERE created_by IS NOT NULL; + +-- FK: commissions.entries.approved_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_entries_approved_by +ON commissions.entries(approved_by) WHERE approved_by IS NOT NULL; + +-- FK: commissions.periods.closed_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_periods_closed_by +ON commissions.periods(closed_by) WHERE closed_by IS NOT NULL; + +-- FK: commissions.periods.paid_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_periods_paid_by +ON commissions.periods(paid_by) WHERE paid_by IS NOT NULL; + +-- FK: commissions.periods.created_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_periods_created_by +ON commissions.periods(created_by) WHERE created_by IS NOT NULL; + +-- ----------------------------------------------------------------------------- +-- Schema: mlm +-- ----------------------------------------------------------------------------- + +-- FK: mlm.structures.created_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_structures_created_by +ON mlm.structures(created_by) WHERE created_by IS NOT NULL; + +-- FK: mlm.nodes.highest_rank_id -> mlm.ranks(id) +CREATE INDEX IF NOT EXISTS idx_nodes_highest_rank +ON mlm.nodes(highest_rank_id) WHERE highest_rank_id IS NOT NULL; + +-- FK: mlm.rank_history.previous_rank_id -> mlm.ranks(id) +CREATE INDEX IF NOT EXISTS idx_rank_history_previous_rank +ON mlm.rank_history(previous_rank_id) WHERE previous_rank_id IS NOT NULL; + +-- ----------------------------------------------------------------------------- +-- Schema: users +-- ----------------------------------------------------------------------------- + +-- FK: users.roles.parent_role_id -> users.roles(id) +CREATE INDEX IF NOT EXISTS idx_roles_parent_role +ON users.roles(parent_role_id) WHERE parent_role_id IS NOT NULL; + +-- FK: users.invitations.role_id -> users.roles(id) +CREATE INDEX IF NOT EXISTS idx_invitations_role +ON users.invitations(role_id) WHERE role_id IS NOT NULL; + +-- FK: users.invitations.accepted_by_user_id -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_invitations_accepted_by +ON users.invitations(accepted_by_user_id) WHERE accepted_by_user_id IS NOT NULL; + +-- FK: users.invitations.created_by -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_invitations_created_by +ON users.invitations(created_by) WHERE created_by IS NOT NULL; + +-- ----------------------------------------------------------------------------- +-- Schema: auth +-- ----------------------------------------------------------------------------- + +-- FK: auth.refresh_tokens.session_id -> auth.sessions(id) +CREATE INDEX IF NOT EXISTS idx_refresh_tokens_session +ON auth.refresh_tokens(session_id) WHERE session_id IS NOT NULL; + +-- ----------------------------------------------------------------------------- +-- Schema: notifications +-- ----------------------------------------------------------------------------- + +-- FK: notifications.delivery_logs.queue_id -> notifications.notification_queue(id) +CREATE INDEX IF NOT EXISTS idx_delivery_logs_queue +ON notifications.delivery_logs(queue_id) WHERE queue_id IS NOT NULL; + +-- FK: notifications.delivery_logs.device_id -> notifications.user_devices(id) +CREATE INDEX IF NOT EXISTS idx_delivery_logs_device +ON notifications.delivery_logs(device_id) WHERE device_id IS NOT NULL; + +-- ----------------------------------------------------------------------------- +-- Schema: feature_flags +-- ----------------------------------------------------------------------------- + +-- FK: feature_flags.flag_evaluations.user_id -> users.users(id) +CREATE INDEX IF NOT EXISTS idx_flag_evaluations_user +ON feature_flags.flag_evaluations(user_id) WHERE user_id IS NOT NULL; + +-- ============================================================================= +-- FIN DE INDICES ADICIONALES PARA FOREIGN KEYS +-- Total: 32 indices creados +-- Nota: El informe original indicaba 31 FKs sin indice (21.7%), pero el +-- analisis detallado revela 32 FKs. Todos han sido cubiertos. +-- =============================================================================