9 errores críticos que todos cometen con SQL (y cómo solucionarlos)

sql

9 errores críticos que todos cometen con SQL (y cómo solucionarlos)

Este artículo va directo al grano: errores reales, cómo detectarlos y soluciones prácticas con ejemplos. Aplícalos hoy en tu base de datos relacional (Postgres, MySQL, SQL Server, etc.).

1. Usar SELECT * en producción

Qué pasa: Recuperas más columnas de las necesarias, aumentas I/O, ancho de banda y acoplas tu aplicación a cambios de esquema.

Cómo detectarlo: Revisar queries lentas o logs de consulta; buscar "SELECT *" en código y procedimientos almacenados.

Solución: Selecciona explícitamente las columnas que necesitas. Mantén DTOs/Views claros y versiona cambios de esquema.

-- Malo
SELECT * FROM orders WHERE customer_id = 42;

-- Bueno
SELECT id, total_amount, created_at FROM orders WHERE customer_id = 42;

2. Índices incorrectos o inexistentes

Qué pasa: Scans completos de tabla, alta latencia en lecturas y bloqueos en escrituras.

Cómo detectarlo: Usa EXPLAIN/EXPLAIN ANALYZE; observa secuencias (seq scan) o tablas grandes con high cost en planes.

Solución: Añade índices a columnas usadas en WHERE, JOIN y ORDER BY; evita índices innecesarios en columnas con alta cardinalidad baja (booleans) y reevalúa índices compuestos.

-- Índice simple
CREATE INDEX idx_orders_customer ON orders(customer_id);

-- Índice compuesto para consultas que filtran por customer y ordenan por fecha
CREATE INDEX idx_orders_customer_date ON orders(customer_id, created_at DESC);

3. No usar transacciones cuando corresponde

Qué pasa: Estados inconsistentes, filas huérfanas, condiciones de carrera en operaciones multi-step.

Cómo detectarlo: Revisa operaciones que modifican múltiples tablas; errores intermitentes en producción tras caídas o retries.

Solución: Encierra operaciones que deben ser atómicas en transacciones y maneja rollback en errores.

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;

4. Olvidar WHERE en UPDATE/DELETE

Qué pasa: Se actualizan o eliminan todas las filas por accidente.

Cómo detectarlo: Revisa logs, auditoría; pruebas unitarias que cubran mutaciones masivas.

Solución: Siempre prueba la cláusula WHERE con SELECT antes de ejecutar la mutación; usa transacciones y límites si aplica; habilita soft-delete o auditoría.

-- Peligroso
DELETE FROM users;

-- Seguro: validar con SELECT, luego eliminar
SELECT id FROM users WHERE last_login < '2020-01-01';
DELETE FROM users WHERE last_login < '2020-01-01';

5. Ignorar EXPLAIN y no perfilar consultas

Qué pasa: Optimización por intuición en lugar de datos; cambios que rompen rendimiento en producción.

Cómo detectarlo: Métricas de latencia crecientes y queries que escalan mal con el volumen de datos.

Solución: Haz EXPLAIN/EXPLAIN ANALYZE antes y después de cambiar índices o queries. Automatiza profiling en staging.

EXPLAIN ANALYZE
SELECT id, total_amount FROM orders WHERE customer_id = 42 ORDER BY created_at DESC LIMIT 10;

6. Manejo incorrecto de NULLs

Qué pasa: Comparaciones inesperadas, joins que omiten filas y conteos erróneos.

Cómo detectarlo: Resultados inconsistentes; diferencias entre COUNT(col) y COUNT(*).

Solución: Diseña columnas con NOT NULL cuando aplique; usa COALESCE en cálculos; comprende tri-logic booleana (TRUE/FALSE/UNKNOWN).

-- Evitar sorpresa en agregaciones
SELECT COUNT(*) FROM table;         -- cuenta todas las filas
SELECT COUNT(column) FROM table;    -- cuenta solo donde column IS NOT NULL

-- Manejar NULLs
SELECT COALESCE(discount, 0) FROM orders;

7. Esquema mal normalizado o sobre-normalizado

Qué pasa: Demasiadas joins (over-normalized) o redundancia y anomalías (under-normalized).

Cómo detectarlo: Consultas complejas con muchas joins; problemas de integridad o duplicación.

Solución: Sigue 3NF como punto de partida; normaliza para integridad y desnormaliza selectivamente para rendimiento, con triggers o procesos ETL para mantener consistencia.

8. N+1 queries (especialmente con ORMs)

Qué pasa: Para cada fila recuperas datos adicionales en queries separados; latencia explosiva.

Cómo detectarlo: Revisa logs de SQL durante rendering de páginas o endpoints; QPS aumenta con nº de filas.

Solución: Usa JOINs, subqueries o técnicas de eager loading del ORM (JOIN FETCH, includes, prefetch_related, etc.).

-- N+1 patrón (pseudo)
for order in orders:
    SELECT * FROM order_items WHERE order_id = order.id;  -- una query por order

-- Solución: traer todo en una sola consulta
SELECT o.id, oi.* FROM orders o LEFT JOIN order_items oi ON oi.order_id = o.id WHERE o.customer_id = 42;

9. No gestionar permisos y exposición de datos

Qué pasa: Aplicaciones que exponen columnas sensibles o permiten consultas malintencionadas por permisos laxos.

Cómo detectarlo: Auditorías de seguridad, revisiones de roles y privilegios, pruebas de penetración.

Solución: Aplica el principio de menor privilegio en DB roles; usa vistas o columnas enmascaradas para exponer solo lo necesario; valida entrada y evita SQL dinámico sin parametrización.

-- Evitar SQL dinámico inseguro
-- Malo
EXECUTE 'SELECT * FROM users WHERE username = "' || input || '"';

-- Bueno: parámetros preparados (ejemplo en pseudo-API)
PREPARE q(text) AS SELECT * FROM users WHERE username = $1;
EXECUTE q('alice');

Aplicando estas correcciones evitarás la mayoría de incidentes operativos y de rendimiento con bases de datos relacionales. Como siguiente paso avanzado: automatiza checks con linters SQL, agrega alertas de queries lentas y crea pruebas de rendimiento que corran en CI. Precaución: los cambios de índices y esquemas en producción requieren pruebas y migraciones planeadas para evitar bloqueos prolongados.

Comentarios
¿Quieres comentar?

Inicia sesión con Telegram para participar en la conversación


Comentarios (0)

Aún no hay comentarios. ¡Sé el primero en comentar!

Iniciar Sesión