7 errores críticos que todos cometen con SQL y cómo solucionarlos

sql 7 errores críticos que todos cometen con SQL y cómo solucionarlos

7 errores críticos que todos cometen con SQL y cómo solucionarlos

Este post va directo al grano: errores frecuentes en SQL que causan bugs, caídas de rendimiento o brechas de seguridad, y cómo arreglarlos con ejemplos prácticos. Para cada error verás por qué sucede, qué daño provoca y cómo solucionarlo (con comandos y snippets reproducibles).

  1. Error 1 — Usar SELECT * en producción

    Por qué pasa: comodidad. Daño: tráfico innecesario, más I/O, peor uso de caché y acoplamiento a cambios de esquema.

    Ejemplo malo:

    SELECT * FROM users WHERE created_at > '2025-01-01';

    Arreglo:

    SELECT id, email, created_at FROM users WHERE created_at > '2025-01-01';

    Por qué funciona: reduces ancho de banda, mejoras tiempos de deserialización y permites seleccionar columnas indexadas cuando conviene.

  2. Error 2 — UPDATE/DELETE sin WHERE o con condición errónea

    Por qué pasa: fat finger, falta de transacciones o tests. Daño: pérdida de datos, downtime.

    Ejemplo peligroso:

    UPDATE orders SET status = 'cancelled'; -- ¡uh-oh!

    Protecciones y arreglos:

    • Probar con SELECT antes: SELECT COUNT(*) FROM orders WHERE ...
    • Usar transacciones y verificaciones:
    BEGIN;
    UPDATE orders SET status = 'cancelled' WHERE id = 123;
    -- comprobar con SELECT
    COMMIT;

    En MySQL habilita binlog_format=ROW y en entornos críticos usa backups/replicas para recovery.

  3. Error 3 — No usar índices o usarlos mal (incluye aplicar funciones en columnas indexadas)

    Por qué pasa: desconocimiento del planner o diseño rápido. Daño: full table scans y latencias altas.

    Ejemplo problemático (Postgres):

    SELECT * FROM events WHERE LOWER(user_email) = 'alice@example.com';

    Problema: función sobre la columna impide usar índice B-tree normal.

    Arreglo 1 — índice funcional:

    CREATE INDEX idx_events_useremail_lower ON events (LOWER(user_email));
    -- ahora la consulta puede usar el índice
    

    Arreglo 2 — normalizar/saneado al insertar (mejor para performance):

    -- guardar user_email_normalized en minúsculas y consultar esa columna
    

    Comprobar con:

    EXPLAIN ANALYZE SELECT ...
  4. Error 4 — Tipos de datos inapropiados e implicit conversions

    Por qué pasa: usar strings para todo o incompatibilidades entre columnas/join keys. Daño: descartes de índices, errores sutiles y desperdicio de almacenamiento.

    Ejemplo:

    -- user_id guardado como text en una tabla y integer en otra
    SELECT * FROM sessions s JOIN users u ON s.user_id = u.id;
    -- esto puede forzar conversión y evitar índices

    Arreglo:

    • Usar tipos adecuados: integers para ids, numeric/decimal para dinero, timestamp/datetime para fechas.
    • Si no puedes cambiar el esquema: convertir explícitamente durante ETL y crear columnas normalizadas.
  5. Error 5 — No usar consultas parametrizadas (riesgo de SQL injection y de plan-cache)

    Por qué pasa: concatenar strings para construir SQL. Daño: vulnerabilidades críticas y planes de ejecución no reutilizables.

    Ejemplo vulnerable (Node.js):

    const q = `SELECT * FROM users WHERE email = '${email}'`;
    await db.query(q);
    

    Arreglo (parametrizado):

    // pg (node-postgres)
    await db.query('SELECT id, email FROM users WHERE email = $1', [email]);
    
    // MySQL (mysql2)
    await db.execute('SELECT id, email FROM users WHERE email = ?', [email]);
    

    Beneficios: evita inyección, mejora caché de planes y separación clara entre lógica y datos.

  6. Error 6 — No inspeccionar el plan de ejecución (no usar EXPLAIN/ANALYZE)

    Por qué pasa: confianza en "parece rápido" o falta de herramientas. Daño: optimizaciones inútiles o peligrosas.

    Uso correcto:

    -- Postgres
    EXPLAIN ANALYZE SELECT ...;
    
    -- MySQL
    EXPLAIN FORMAT=JSON SELECT ...;
    

    Qué buscar: scans en tablas grandes, cost/high rows estimates, joins en bucle anidado donde quieras hash/merge, uso de índices inexistente. Antes de añadir índices, verifica con EXPLAIN si el planner los usaría.

  7. Error 7 — Transacciones demasiado largas o mal entendidas las isolation levels

    Por qué pasa: opens/long-running transactions por errores de diseño o polling dentro de una tx. Daño: locks prolongados, bloating (VACUUM/GC), contención y latencia.

    Ejemplos de malas prácticas:

    BEGIN;
    -- hacer operaciones de red/IO lentas aquí
    UPDATE ...;
    -- esperar respuesta HTTP externa
    COMMIT;

    Arreglos

    • Minimizar el trabajo dentro de la transacción: solo operaciones DB necesarias.
    • Batch y chunking para operaciones masivas:
    -- ejemplo de batching
    -- pseudo: procesar 1000 filas por batch
    WHILE (1) {
      WITH cte AS (
        SELECT id FROM big_table WHERE processed = false LIMIT 1000 FOR UPDATE SKIP LOCKED
      )
      UPDATE big_table SET processed = true WHERE id IN (SELECT id FROM cte);
      -- commit y continuar
    }
    

    Evita long transactions en Postgres para impedir bloat; en MySQL/InnoDB ten cuidado con gap locks en REPEATABLE READ.

Checklist rápido para producción

  • No usar SELECT *.
  • Probar condiciones de UPDATE/DELETE con SELECT y envolver en transacciones si es necesario.
  • Crear índices pensando en consultas críticas; evita funciones en columnas a menos que indexes la forma funcional.
  • Usar tipos de datos correctos desde el diseño del esquema.
  • Usar consultas parametrizadas en la capa de aplicación.
  • Revisar el plan con EXPLAIN ANALYZE antes y después de cambios significativos.
  • Evitar transacciones largas; procesar en batches y usar FOR UPDATE SKIP LOCKED cuando corresponda.

Comandos y herramientas recomendadas

  • EXPLAIN / EXPLAIN ANALYZE (Postgres). MySQL: EXPLAIN FORMAT=JSON.
  • pg_stat_statements (Postgres) para identificar queries frecuentes y costosas.
  • pt-query-digest (Percona) para análisis de queries MySQL.
  • Herramientas APM (NewRelic, Datadog) para correlacionar queries con latencia de aplicaciones.

Siguiente paso: elige una de tus consultas más críticas, ejecútala con EXPLAIN ANALYZE, revisa si hay full scans o filas examinadas en exceso, y aplica uno de los arreglos anteriores (index funcional, parametrización, batch o cambio de tipo). Un consejo avanzado: habilita métricas de long-running transactions en tu monitoring y alerta cuando superen X segundos — eso suele descubrir problemas de bloqueo antes de que afecten al SLA.

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