7 errores críticos que todos cometen con SQL (y cómo arreglarlos)
Este artículo señala errores frecuentes en SQL —tanto de rendimiento como de seguridad y lógica— con ejemplos prácticos, cómo detectarlos y soluciones concretas. Ve al grano: código, por qué falla y la corrección.
1) Usar SELECT * en producción
Problema: traer columnas que no necesitas aumenta I/O, red y CPU, y hace que cambios en el esquema afecten consultas.
-- Malo
SELECT * FROM users WHERE status = 'active';
-- Mejor: solo lo necesario
SELECT id, email, last_login FROM users WHERE status = 'active';
Por qué: reduces ancho de banda y lecturas de disco, y mejoras uso de índices (si los índices cubren las columnas seleccionadas).
2) No tener (o tener mal) índices
Problema: consultas que escanean la tabla completa (table scan). Detecta con EXPLAIN o EXPLAIN ANALYZE.
-- Malo: sin índice en user_id
SELECT * FROM orders WHERE user_id = 12345;
-- Solución: crear índice
CREATE INDEX idx_orders_user_id ON orders(user_id);
Por qué: los índices aceleran búsquedas, JOINs y ORDER BY. Pero cuidado: demasiados índices afectan INSERT/UPDATE/DELETE y ocupan espacio.
3) No usar transacciones para operaciones múltiples
Problema: en operaciones que requieren atomicidad (transferencias, inserciones relacionadas), si algo falla quedas con datos inconsistentes.
-- Malo: operaciones separadas
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- Bueno: transacción
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- En caso de error: ROLLBACK
Por qué: las transacciones garantizan atomicidad, consistencia y aislamiento (ACID). Usa niveles de aislamiento apropiados según la necesidad.
4) Construir consultas concatenando strings (riesgo de SQL Injection)
Problema: entradas del usuario inyectadas en la consulta permiten ejecución arbitraria.
-- Muy malo (vulnerable)
query = "SELECT * FROM users WHERE email = '" + email + "'";
-- Correcto: consultas parametrizadas
-- Ejemplo con PDO (PHP)
$stmt = $pdo->prepare('SELECT id, email FROM users WHERE email = ?');
$stmt->execute([$email]);
$user = $stmt->fetch();
Por qué: usar parámetros evita que datos de usuario sean interpretados como SQL. En ORMs y drivers modernos siempre usa parámetros/prepared statements.
5) Ignorar NULLs y comparaciones erróneas
Problema: comparar NULL con = o asumir que NULL = NULL es verdadero produce resultados inesperados.
-- Malo
SELECT * FROM products WHERE discontinued = NULL; -- nunca retornará filas
-- Correcto
SELECT * FROM products WHERE discontinued IS NULL;
-- Para actualizar evitando NULLs
UPDATE products SET stock = COALESCE(stock, 0) + 5 WHERE id = 1;
Por qué: NULL representa información desconocida; usa IS NULL/IS NOT NULL y funciones como COALESCE para controlarlo.
6) Patrón N+1 en aplicaciones (consultas repetidas por fila)
Problema: una consulta principal seguida de N consultas adicionales dentro de un bucle —multiplica llamadas a la DB y degrada rendimiento.
-- Malo: por cada post haces una consulta para autor
SELECT * FROM posts LIMIT 20;
-- en código: por cada post -> SELECT * FROM users WHERE id = post.author_id;
-- Mejor: hacer JOIN o batch
SELECT p.id, p.title, u.id AS author_id, u.name
FROM posts p
JOIN users u ON u.id = p.author_id
LIMIT 20;
Por qué: reduce round-trips y aprovecha la capacidad de la DB para combinar datos eficientemente. Si usas ORM, aprende sus mecanismos de eager loading (JOIN/fetch).
7) ORDER BY/GROUP BY sin soporte de índice o en grandes volúmenes
Problema: ordenar o agrupar columnas grandes provoca ordenamiento en disco y consumo de recursos.
-- Malo: ordenando por texto largo sin índice adecuado
SELECT * FROM logs ORDER BY created_at DESC LIMIT 100;
-- Mejor: índice compuesto si filtras y ordenas por columnas relacionadas
CREATE INDEX idx_logs_service_created ON logs(service, created_at DESC);
-- Para GROUP BY: asegúrate de agrupar por columnas indexadas o preagregar
SELECT user_id, COUNT(*) FROM events GROUP BY user_id;
Por qué: un índice adecuado puede cubrir ORDER BY y acelerar GROUP BY; en consultas analíticas, considera materialized views o tablas de agregados.
Cómo detectar estos problemas
- Usa
EXPLAIN/EXPLAIN ANALYZEpara revisar planes de ejecución. - Activa el slow query log (MySQL) o revisa pg_stat_statements (Postgres).
- Monitorea latencias y número de queries por endpoint (APM, logs).
- Simula carga y perfila con datos reales o representativos.
Checklist rápido para producción
- No usar
SELECT *. - Revisar y mantener índices (evitar índices innecesarios).
- Usar transacciones donde convenga.
- Preparar consultas y parametrizar siempre entradas de usuarios.
- Manejar NULL explícitamente.
- Evitar N+1 usando JOINs o eager loading en ORMs.
- Optimizar ORDER BY/GROUP BY o usar agregaciones precomputadas.
Consejo avanzado: cuando optimices, empieza por medir. Usa EXPLAIN ANALYZE para ver tiempos reales y revisa estadísticas del servidor (I/O, locks, buffers). Implementa cambios incrementales y verifica efecto en las métricas. Advertencia: añadir un índice puede mejorar lecturas pero penalizar escrituras; evalúa trade-offs según tu carga.
Siguiente paso sugerido: identifica 3 consultas lentas hoy, revisa su plan y aplica estas correcciones una por una; documenta resultados y ajusta índices en base a datos reales.
¿Quieres comentar?
Inicia sesión con Telegram para participar en la conversación