Estructura de un SELECT
El orden lógico de evaluación es: FROM → JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT. Es contraintuitivo: aunque escribís SELECT primero, se ejecuta casi al final. Esto explica por qué no podés usar un alias definido en SELECT dentro del WHERE de la misma query.
JOINs explícitos vs implícitos
Antes era común FROM a, b WHERE a.id = b.a_id. Hoy lo correcto es
FROM a JOIN b ON a.id = b.a_id. La sintaxis explícita separa la condición
de unión de los filtros, evita productos cartesianos accidentales y deja claro el tipo de
join (INNER, LEFT, RIGHT, FULL).
WHERE vs HAVING
WHERE filtra filas antes de agrupar; HAVING filtra grupos después. Si no usás GROUP BY,
usá WHERE. HAVING COUNT(*) > 10 tiene sentido; HAVING age > 18
sin GROUP BY es probablemente un error.
Subqueries vs JOINs vs CTEs
Para datos que vas a usar múltiples veces, una CTE (WITH x AS (SELECT ...))
es más legible que repetir la subquery. Los planificadores modernos suelen optimizarlas
igual que subqueries inline. Para casos simples, un JOIN suele ser más rápido y claro
que una subquery.
Window functions
ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD y agregaciones con OVER permiten cálculos por
partición sin colapsar filas. Para "top 3 productos por categoría", usás
ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) y filtrás
donde el row_number sea ≤ 3 (en una CTE).
Performance: índices y EXPLAIN
Antes de optimizar, corré EXPLAIN ANALYZE (Postgres) o EXPLAIN
(MySQL). Buscá seq scans sobre tablas grandes, sorts sin índice y nested loops
con muchas iteraciones. La regla de oro: indexá las columnas usadas en JOIN y WHERE; en
consultas con ORDER BY + LIMIT, un índice por la columna de orden ayuda.
SELECT * es un anti-patrón
Trae todas las columnas, transfiere bytes que no usás, evita índices cubiertos y rompe si alguien agrega una columna sensible. Listá explícitamente las columnas que necesitás. Genfy genera SELECT explícito en cada ejemplo.
Inyección SQL
Nunca concatenes input del usuario en la query. Usá parámetros ($1, $2 en
Postgres, ? en MySQL) o un ORM que los maneje por vos. Las queries generadas
acá son ejemplos para entender; en producción usá prepared statements.