SQL Cheat Sheet: Consultas clave, joins y optimización básica
·
3 min de lectura · 14 lecturas
Guía práctica de referencia con la sintaxis esencial de SQL: tipos de JOINs explicados, subconsultas, diseño de índices y comandos de diagnóstico para PostgreSQL y MySQL.
SQL Cheat Sheet: Consultas Clave, Joins y Optimización Básica
Una referencia directa y estructurada para el trabajo diario con bases de datos relacionales. Cubre desde operaciones fundamentales y cruces de tablas hasta optimización con índices y diagnósticos rápidos de rendimiento.
1. Orden de Ejecución de una Consulta
Para depurar y optimizar consultas complejas es clave entender que SQL no ejecuta las cláusulas en el orden en que se escriben:
FROM / JOIN: Se identifican y cruzan las tablas origen.
WHERE: Se filtran filas a nivel individual.
GROUP BY: Se agrupan los registros resultantes.
HAVING: Se filtran los grupos creados.
SELECT: Se evalúan expresiones y proyecciones.
DISTINCT: Se eliminan filas duplicadas.
ORDER BY: Se ordenan los resultados.
LIMIT / OFFSET: Se pagina el conjunto devuelto.
2. Tipos de JOIN
Considera dos entidades: usuarios (id, nombre) y pedidos (id, usuario_id, total).
INNER JOIN
Retorna únicamente los registros que coinciden en ambas tablas.
sql
SELECT u.nombre, p.totalFROM usuarios uINNER JOIN pedidos p ON u.id = p.usuario_id;
LEFT JOIN (LEFT OUTER JOIN)
Retorna todos los registros de la tabla izquierda y las coincidencias de la derecha (o NULL si no hay relación).
sql
SELECT u.nombre, p.totalFROM usuarios uLEFT JOIN pedidos p ON u.id = p.usuario_id;
Tip práctico: Para encontrar "usuarios sin pedidos":
sql
> SELECT u.nombre> FROM usuarios u> LEFT JOIN pedidos p ON u.id = p.usuario_id> WHERE p.id IS NULL;>
RIGHT JOIN
Retorna todos los registros de la tabla derecha y las coincidencias de la izquierda. Rara vez se usa; suele reescribirse como LEFT JOIN invirtiendo el orden de las tablas por claridad.
FULL OUTER JOIN
Retorna todos los registros cuando hay coincidencia en cualquiera de las dos tablas (rellena con NULL donde falte el par).
sql
-- Soportado nativamente en PostgreSQL:SELECT u.nombre, p.totalFROM usuarios uFULL OUTER JOIN pedidos p ON u.id = p.usuario_id;
3. Subconsultas y CTEs (Common Table Expressions)
Subconsulta en WHERE (con IN / EXISTS)
Prefiere EXISTS sobre IN cuando la subconsulta maneje grandes volúmenes de datos, ya que el motor puede detener la búsqueda al encontrar la primera coincidencia:
sql
SELECT nombreFROM usuarios uWHERE EXISTS ( SELECT 1 FROM pedidos p WHERE p.usuario_id = u.id AND p.total > 1000);
CTEs (WITH): Código más legible
Las CTEs reemplazan subconsultas anidadas difíciles de mantener por bloques reutilizables:
sql
WITH pedidos_recientes AS ( SELECT usuario_id, SUM(total) AS total_gastado FROM pedidos WHERE fecha >= CURRENT_DATE - INTERVAL '30 days' -- PostgreSQL GROUP BY usuario_id)SELECT u.nombre, pr.total_gastadoFROM usuarios uINNER JOIN pedidos_recientes pr ON u.id = pr.usuario_idWHERE pr.total_gastado > 5000;
4. Agregación y Window Functions
Agrupamiento y filtro de grupos
WHERE filtra filas antes de agrupar; HAVING filtra después de agrupar:
sql
SELECT usuario_id, COUNT(*) AS total_pedidos, AVG(total) AS promedioFROM pedidosWHERE estado = 'COMPLETADO'GROUP BY usuario_idHAVING COUNT(*) >= 5;
Funciones de Ventana (ROW_NUMBER, RANK)
Permiten calcular sobre particiones sin colapsar las filas resultantes:
sql
-- Obtener el pedido más reciente por cada usuarioWITH ranking_pedidos AS ( SELECT id, usuario_id, total, ROW_NUMBER() OVER (PARTITION BY usuario_id ORDER BY fecha DESC) as rn FROM pedidos)SELECT id, usuario_id, totalFROM ranking_pedidosWHERE rn = 1;
5. Estrategia e Índices
B-Tree (Índice Estándar)
Ideal para comparaciones de igualdad (=), rangos (<, <=, >, >=) y ordenamiento (ORDER BY):
sql
-- PostgreSQL / MySQLCREATE INDEX idx_pedidos_usuario_fecha ON pedidos (usuario_id, fecha DESC);
Regla de oro del índice compuesto (Leftmost Prefix): Un índice en (colA, colB) acelera filtros en colA o en colA AND colB, pero no acelerará un filtro que consulte únicamente colB.
Índices Parciales / Filtrados (PostgreSQL)
Ahorran espacio en disco y memoria RAM si solo consultas un subconjunto específico:
sql
-- Solo indexa pedidos activos, ignorando el historial completado o canceladoCREATE INDEX idx_pedidos_pendientesON pedidos (fecha) WHERE estado = 'PENDIENTE';
6. Diagnóstico y Optimización de Consultas
Analizar planes de ejecución
Nunca asumas cómo ejecutará la base de datos tu consulta; inspecciona el costo:
En PostgreSQL:
sql
-- EXPLAIN ANALYZE ejecuta la consulta y muestra tiempos realesEXPLAIN (ANALYZE, BUFFERS)SELECT * FROM pedidos WHERE usuario_id = 42 ORDER BY fecha DESC;
Seq Scan (Sequential Scan): Recorre toda la tabla fila por fila. Si la tabla es grande, evalúa crear un índice.
Index Scan / Index Only Scan: Acceso eficiente mediante árbol de índice.
En MySQL:
sql
EXPLAIN FORMAT=TREESELECT * FROM pedidos WHERE usuario_id = 42;
Revisa la columna type: ALL (escaneo completo de tabla, evitar si es grande), ref o const (búsqueda rápida por índice).
7. Mantenimiento Rápido y Salud de Tablas
Con el paso del tiempo, las inserciones, actualizaciones (UPDATE) y borrados (DELETE) dejan espacio muerto o estadísticas desactualizadas que confunden al optimizador del motor.
PostgreSQL
sql
-- Actualiza estadísticas para que el optimizador elija el mejor plan:ANALYZE VERBOSE pedidos;-- Recupera espacio de tuplas muertas y recalcula estadísticas:VACUUM ANALYZE pedidos;-- Ver tamaño de tablas e índices:SELECT relname AS tabla, pg_size_pretty(pg_total_relation_size(relid)) AS tamano_totalFROM pg_catalog.pg_statio_user_tablesORDER BY pg_total_relation_size(relid) DESC;
MySQL / MariaDB
sql
-- Actualiza estadísticas de índices:ANALYZE TABLE pedidos;-- Desfragmenta y recupera espacio no utilizado:OPTIMIZE TABLE pedidos;-- Ver procesos en ejecución y queries lentas activas:SHOW PROCESSLIST;
Buenas Prácticas Rápidas
Evita SELECT * en producción: Solicita únicamente las columnas requeridas para reducir uso de red y permitir Index Only Scans.
Cuidado con funciones sobre columnas indexadas:WHERE YEAR(fecha) = 2026 invalida el índice en fecha. Usa rangos: WHERE fecha >= '2026-01-01' AND fecha < '2027-01-01'.
Limita siempre queries exploratorias: Evita bloqueos y consumo accidental de memoria añadiendo LIMIT 50 en entornos vivos.