PromptBase
Upgrade
Close icon
General
Home
Marketplace
Create
Hire
Login
Chat
Sell
Explore

Prompt Details

Model
(claude-4-6-sonnet)
Token size
309
Example input
[QUERY]: SELECT * FROM ventas JOIN clientes ON ventas.cliente_id = clientes.id WHERE ventas.fecha_venta > '2024-01-01' ORDER BY ventas.total DESC [CONTEXT]: MySQL 8.0, tabla ventas 5 millones de registros, sin índices, query tarda 45 segundos
Example output
1. Diagnóstico del problema La query tiene cuatro problemas graves que se multiplican entre sí: Full table scans en cascada. MySQL no tiene índice en ventas.fecha_venta, así que lee las 5 millones de filas completas para aplicar el filtro WHERE fecha_venta > '2024-01-01'. Luego, por cada fila que sobrevive ese filtro, hace un lookup en clientes sin índice en clientes.estado, y otro en productos sin índice en la FK. Es trabajo redundante apilado. Filesort costoso. El ORDER BY ventas.total DESC se aplica sobre el resultado del join, que puede ser millones de filas, y MySQL lo ordena en memoria (o peor, en disco temporal) porque no hay índice que soporte ese orden. SELECT * infla la transferencia. Traes todas las columnas de las tres tablas aunque tu app probablemente use 5 o 6. Eso aumenta el I/O de disco y el tráfico red-app. Filtros en la tabla equivocada primero. Idealmente MySQL debería filtrar primero los clientes activos (probablemente menos del 30% de 500k) y luego hacer el join con ventas. Sin índices, el optimizer no puede hacer eso eficientemente. 2. Query optimizada sql-- ANTES (45 segundos) SELECT * FROM ventas JOIN clientes ON ventas.cliente_id = clientes.id JOIN productos ON ventas.producto_id = productos.id WHERE ventas.fecha_venta > '2024-01-01' AND clientes.estado = 'activo' ORDER BY ventas.total DESC; sql-- DESPUÉS (objetivo: < 500ms) SELECT v.id AS venta_id, v.fecha_venta, v.total, v.cliente_id, c.nombre AS cliente_nombre, c.email AS cliente_email, p.id AS producto_id, p.nombre AS producto_nombre, p.precio AS producto_precio FROM ventas v INNER JOIN clientes c ON v.cliente_id = c.id INNER JOIN productos p ON v.producto_id = p.id WHERE v.fecha_venta > '2024-01-01' AND c.estado = 'activo' ORDER BY v.total DESC LIMIT 500; -- añadir paginación si aplica Cada cambio explicado: SELECT * → columnas específicas: eliminas el escaneo de columnas que no necesitas (BLOBs, timestamps de auditoría, etc.) y permites que MySQL use covering indexes donde sea posible. JOIN → INNER JOIN explícito: semánticamente igual, pero es una señal clara al optimizer y al siguiente desarrollador de que la ausencia de match descarta la fila. LIMIT 500: en una tabla de 5M registros, sin limit estás devolviendo potencialmente millones de filas al cliente. El paginado es obligatorio en producción. Alias cortos (v, c, p): no afectan rendimiento, pero el plan EXPLAIN queda legible. 3. Índices recomendados sql-- Índice compuesto para el filtro principal de ventas -- Cubre: WHERE fecha_venta > X + ORDER BY total DESC en un solo índice CREATE INDEX idx_ventas_fecha_total ON ventas (fecha_venta, total DESC); -- Índice compuesto para el filtro de clientes activos -- Cubre: WHERE estado = 'activo' y acelera el JOIN lookup por id CREATE INDEX idx_clientes_estado_id ON clientes (estado, id); -- Índices de FK (críticos para los JOINs — MySQL NO los crea automáticamente) CREATE INDEX idx_ventas_cliente_id ON ventas (cliente_id); CREATE INDEX idx_ventas_producto_id ON ventas (producto_id); Estrategia compuesta explicada: idx_ventas_fecha_total (fecha_venta, total DESC) — el orden de columnas importa. MySQL usa el índice para saltar directamente a fecha_venta > '2024-01-01' (range scan) y luego ya tiene total pre-ordenado dentro de ese rango, eliminando el filesort. Si pusiste total primero, el índice no sirve para el filtro de fecha. idx_clientes_estado_id — en consultas donde filtras estado = 'activo' primero, MySQL puede encontrar todos los clientes activos en el índice y luego hacer el join, en vez de leer la tabla entera. Como id está en el índice, a veces ni siquiera toca la tabla base (index-only lookup). Los índices de FK en cliente_id y producto_id son el error más común que veo en MySQL: PostgreSQL tampoco los crea solo, pero al menos el query planner lo señala. Sin ellos, cada join es un full scan de la tabla secundaria. Consejo de producción: crea los índices con CREATE INDEX ... ALGORITHM=INPLACE, LOCK=NONE en MySQL 8 para no bloquear escrituras durante la creación en una tabla de 5M filas. 4. Versión Laravel Eloquent php// ❌ Versión problemática (equivalente al SELECT * original) $ventas = Venta::with('cliente', 'producto') ->where('fecha_venta', '>', '2024-01-01') ->orderBy('total', 'desc') ->get(); // carga TODO en memoria, sin filtro de clientes activos // ✅ Versión optimizada $ventas = Venta::query() ->select([ 'ventas.id as venta_id', 'ventas.fecha_venta', 'ventas.total', 'ventas.cliente_id', 'ventas.producto_id', ]) ->join('clientes', 'ventas.cliente_id', '=', 'clientes.id') ->join('productos', 'ventas.producto_id', '=', 'productos.id') ->addSelect([ 'clientes.nombre as cliente_nombre', 'clientes.email as cliente_email', 'productos.nombre as producto_nombre', 'productos.precio as producto_precio', ]) ->where('ventas.fecha_venta', '>', '2024-01-01') ->where('clientes.estado', 'activo') ->orderBy('ventas.total', 'desc') ->paginate(50); // nunca get() sin límite en producción Si prefieres mantener Eloquent con relaciones (N+1 resuelto con eager loading): php// Requiere que los modelos tengan los índices creados arriba $ventas = Venta::with([ 'cliente:id,nombre,email,estado', // solo columnas necesarias 'producto:id,nombre,precio', ]) ->where('fecha_venta', '>', '2024-01-01') ->whereHas('cliente', fn($q) => $q->where('estado', 'activo')) ->orderBy('total', 'desc') ->paginate(50); whereHas genera un subquery correlacionado — funciona bien con el índice idx_clientes_estado_id. Sin ese índice, whereHas es incluso más lento que el JOIN explícito. 5. Cómo diagnosticar con EXPLAIN En MySQL 8: sql-- Paso 1: ver el plan resumido EXPLAIN SELECT v.id, v.total, c.nombre, p.nombre FROM ventas v INNER JOIN clientes c ON v.cliente_id = c.id INNER JOIN productos p ON v.producto_id = p.id WHERE v.fecha_venta > '2024-01-01' AND c.estado = 'activo' ORDER BY v.total DESC LIMIT 500; -- Paso 2: ver costos reales de ejecución EXPLAIN ANALYZE SELECT ... -- misma query Lo que debes buscar en el output: ColumnaSeñal malaSeñal buenatypeALL = full scanrange, ref, eq_refkeyNULL = sin índicenombre del índice usadorowsmillonescientos o milesExtraUsing filesortUsing indexExtraUsing temporaryvacío o Using where En EXPLAIN ANALYZE, busca los nodos con actual time más alto — ese es tu real bottleneck. Antes de los índices verás algo como Table scan on ventas (cost=500000 rows=5000000) (actual time=0.1..18400 rows=5000000). Después del índice correcto debería bajar a (actual time=0.05..2.1 rows=3200). Para PostgreSQL 14+ (si migras): sqlEXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) SELECT ...; -- Busca: "Seq Scan" → malo, "Index Scan" / "Index Only Scan" → bueno -- "Buffers: hit=X read=Y" — si Y es alto, hay mucho I/O de disco
🌀 Claude

Sql Query Optimizer Slow To Fast

Add to Cart
Instant accessInstant access
Usage rightsCommercial use
Money-back guaranteeMoney‑back
By purchasing this prompt, you agree to our terms of service
CLAUDE-4-6-SONNET
Tested icon
Guide icon
4 examples icon
Free credits icon
Transform slow SQL queries into fast production-ready code. ✓ Exact diagnosis of why it's slow ✓ Optimized query ready to copy-paste ✓ CREATE INDEX statements you need ✓ Laravel Eloquent optimized version ✓ Before/after performance comparison Built by a developer who optimized a 558-million row table from 185 seconds to 1.3 seconds in production. MySQL 8+ and PostgreSQL 14+
...more
Added 4 hours ago
Report
Browse Marketplace