Saltar al contenido

Parte 04 — SQL en profundidad

Escribir SQL cuya semántica se pueda defender: definición del esquema, reuniones, agregación, ventanas y el comportamiento real de los nulos.

6 clases · 20 horas · 27 conceptos · 9 fuentes

Antes de esta parte

Esta parte se apoya en lo trabajado antes. Si vienes de fuera del programa, revisa al menos el vocabulario de:

De qué trata esta parte

Seis clases para escribir SQL cuya semántica se pueda defender. No es un recorrido por la sintaxis: es la parte donde se cierran los huecos por los que se cuelan los resultados silenciosamente incorrectos, que son mucho peores que los errores, porque no fallan.

El orden sigue el ciclo de vida de una consulta. Primero el DDL como contrato ejecutable, porque lo que el esquema rechaza no hay que validarlo en ningún lenguaje. Después el `SELECT` con su orden lógico de evaluación y la colación, que decide si `a` es igual a `A`. Luego las cuatro formas de reunir, con la semirreunión como la corrección que más código arregla. La agregación y el doble conteo que produce una reunión previa. Las CTE, la recursión y las funciones de ventana, para lo que no cabe en una expresión. Y al final los nulos, colocados a propósito al cierre para que se vean actuando sobre todo lo anterior.

Si solo hay tiempo para dos clases de esta parte, son la 026 y la 029: reuniones y nulos concentran la mayoría de los errores que llegan a producción sin ser detectados.

Al terminar esta parte podrás

  1. Escribir DDL que exprese las reglas del dominio como restricciones que el motor impone.
  2. Predecir el resultado de una consulta razonando desde el orden lógico de evaluación.
  3. Elegir entre reunión interna, externa, semi y anti según lo que la pregunta realmente pide.
  4. Detectar y corregir un doble conteo causado por una reunión que multiplica filas.
  5. Resolver con CTE, recursión o funciones de ventana lo que no cabe en una consulta plana.
  6. Predecir el efecto de los nulos en comparaciones, agregados y `NOT IN`.

Las clases, una por una

#ClaseNivelHorasFuentes
024DDL: el esquema como contrato ejecutableFundamentos34
025SELECT: filtrado, proyección y orden con semántica precisaFundamentos33
026Reuniones: interna, externa, semi y antiIntermedio43
027Agregación, GROUP BY y HAVING sin duplicar filasIntermedio33
028CTE, subconsultas y funciones de ventanaIntermedio44
029Nulos y lógica de tres valoresIntermedio33

024 — DDL: el esquema como contrato ejecutable

Fundamentos · 3 h · 4 fuentes · requiere 006, 023

El DDL leído como un contrato ejecutable: cada tipo y cada restricción es una validación que ya no hay que escribir en ninguna aplicación. Incluye una divergencia que decide cómo se hacen las migraciones: si el DDL es transaccional en tu motor o si una migración a medias deja un estado sin retorno.

tipo de dato restricción valor por defecto DDL transaccional

025 — SELECT: filtrado, proyección y orden con semántica precisa

Fundamentos · 3 h · 3 fuentes · requiere 004, 020

El `SELECT` con su semántica exacta: el orden lógico de evaluación que explica por qué un alias del `SELECT` no vale en el `WHERE`, y la colación, que decide si `a` es igual a `A` y dónde va la `ñ`. Cierra con el determinismo de orden, que es la diferencia entre una consulta reproducible y una que cambia el día que cambia el plan.

predicado orden de evaluación colación determinismo de orden

026 — Reuniones: interna, externa, semi y anti

Intermedio · 4 h · 3 fuentes · requiere 008, 021

Las cuatro formas de reunir y cuándo se quiere cada una. La distinción que más código corrige es la semirreunión: el `EXISTS` que casi siempre se quería cuando se escribió un `JOIN` seguido de `DISTINCT`, sin multiplicar filas ni arriesgar el doble conteo.

reunión interna reunión externa semirreunion antirreunion multiplicación de filas

027 — Agregación, GROUP BY y HAVING sin duplicar filas

Intermedio · 3 h · 3 fuentes · requiere 026

Agregar sin mentir. Explica por qué `WHERE` y `HAVING` no son intercambiables, cómo una reunión previa multiplica filas y produce totales inflados, y cómo se corrige agregando en una CTE antes de reunir. Incluye la divergencia entre motores sobre qué columnas se pueden seleccionar sin agrupar.

agrupación agregado HAVING doble conteo dependencia funcional en GROUP BY

028 — CTE, subconsultas y funciones de ventana

Intermedio · 4 h · 4 fuentes · requiere 026, 027

Las herramientas para consultas que no caben en una sola expresión: CTE para nombrar pasos, recursión para recorrer jerarquías y funciones de ventana para calcular por grupo sin perder el detalle. La sutileza que más resultados cambia es el marco por defecto de una ventana con `ORDER BY`.

CTE recursión subconsulta correlacionada partición de ventana marco

029 — Nulos y lógica de tres valores

Intermedio · 3 h · 3 fuentes · requiere 003, 025

La lógica de tres valores y sus consecuencias prácticas: `NOT IN` que devuelve vacío por un solo nulo, agregados que ignoran ausencias y comparaciones que nunca son ciertas. Presenta `IS DISTINCT FROM` como la forma correcta de comparar columnas opcionales, que reaparece al detectar cambios en una migración.

UNKNOWN IS DISTINCT FROM NOT IN con nulos agregados y nulos

Errores frecuentes en esta parte

Cada uno de estos es una creencia habitual y su corrección.

Vocabulario de la parte

Los 27 términos que esta parte introduce. Todos están también en el glosario del programa con sus términos relacionados.

TérminoQué significaSe trabaja en
agregadoConjunto de datos que se trata como una unidad para leer, escribir y garantizar consistencia: un pedido con sus líneas. Sadalage y Fowler lo toman del diseño dirigido por el dominio y lo convierten en el criterio que separa a los motores NoSQL del relacional. (En la clase 027 la palabra se usa en su otro sentido: el resultado de una función de agregación como `SUM` o `COUNT`.)027
agregados y nulosLas funciones de agregado ignoran los nulos, salvo `COUNT(*)` que cuenta filas. Por eso `COUNT(columna)` y `COUNT(*)` difieren, y por eso un `AVG` sobre una columna con huecos es la media de los presentes, no del total.029
agrupaciónPartir las filas en grupos por los valores de unas columnas (`GROUP BY`) y producir una fila de resultado por grupo. Todo lo que aparezca en el `SELECT` debe ser o columna de agrupación o resultado de una función de agregado.027
antirreunionQuedarse con las filas que *no* tienen pareja: `NOT EXISTS`, o `LEFT JOIN … WHERE clave IS NULL`. `NOT IN` parece equivalente y no lo es: basta un nulo en la subconsulta para que devuelva el conjunto vacío.026
colaciónEl conjunto de reglas que decide cómo se comparan y ordenan los textos: si `a` = `A`, dónde va la `ñ`, si los acentos cuentan. Cambia el resultado de `ORDER BY`, de `=` y de un `UNIQUE`, y es distinta por defecto en cada motor.025
CTEExpresión de tabla común (`WITH … AS`): un resultado con nombre, visible en la consulta que la sigue. Sirve para nombrar pasos intermedios y hacer legible una consulta larga; en algunos motores es además una barrera de optimización, y eso puede ayudar o estorbar.028
DDL transaccionalCapacidad de ejecutar `CREATE`, `ALTER` o `DROP` dentro de una transacción y poder revertirlos. PostgreSQL y SQLite la tienen; MySQL histórico y Oracle confirman implícitamente, lo que convierte una migración fallida a mitad en un estado sin retorno.024
dependencia funcional en GROUP BYRegla que permite seleccionar una columna no agrupada si depende funcionalmente de la clave de agrupación —agrupar por `id` y seleccionar `nombre`—. PostgreSQL la reconoce; otros motores exigen listar todo, y MySQL en modo laxo devuelve un valor arbitrario sin avisar.027
determinismo de ordenQue dos ejecuciones de la misma consulta devuelvan las filas en el mismo orden. Solo lo garantiza un `ORDER BY` cuyas columnas no empaten; con empates, el desempate lo decide el plan y puede cambiar mañana.025
doble conteoSumar o contar sobre un resultado que una reunión ya había multiplicado. El síntoma es un total que crece al añadir un `JOIN` que «solo traía un dato más»; la cura es agregar en una subconsulta o CTE antes de reunir.027
HAVINGFiltro que se aplica a los grupos ya formados, después de agregar. `WHERE` descarta filas antes de agrupar y por eso es más barato: la regla es filtrar en `WHERE` todo lo que no dependa del agregado.027
IS DISTINCT FROMComparación que trata el nulo como un valor más: dos nulos son iguales y un nulo es distinto de cualquier valor, sin producir `UNKNOWN`. Es la forma correcta de comparar columnas opcionales, por ejemplo al detectar cambios en una migración.029
marcoEl `ROWS`/`RANGE BETWEEN` que define qué filas de la partición entran en el cálculo de cada fila. Su valor por defecto no es «toda la partición» cuando hay `ORDER BY`, y esa sutileza cambia el resultado de una suma acumulada.028
multiplicación de filasEfecto de reunir con una tabla que tiene varias filas por clave: cada fila del lado uno aparece repetida. Es la causa del doble conteo cuando después se suma, y la razón de que agregar antes de reunir sea a menudo la corrección.026
NOT IN con nulosTrampa clásica: si la lista o la subconsulta de un `NOT IN` contiene un solo nulo, el predicado nunca es verdadero y el resultado es vacío. `NOT EXISTS` no tiene ese problema y es la sustitución recomendada.029
orden de evaluaciónEl orden lógico en que SQL procesa una consulta: `FROM`, `WHERE`, `GROUP BY`, `HAVING`, `SELECT`, `ORDER BY`, `LIMIT`. Explica por qué no se puede usar un alias del `SELECT` en el `WHERE` y por qué `HAVING` filtra grupos y `WHERE` filtra filas.025
partición de ventanaEl `PARTITION BY` de una función de ventana: divide las filas en grupos para calcular el agregado dentro de cada uno, pero sin colapsarlas. Es la diferencia esencial con `GROUP BY`: la ventana conserva el detalle y añade el cálculo al lado.028
predicadoExpresión lógica que se evalúa a verdadero, falso o desconocido para cada fila. En SQL solo pasan el filtro las filas cuyo predicado es verdadero: `UNKNOWN` se descarta igual que `FALSE`, y ahí empiezan los resultados sorprendentes con nulos.025
recursión`WITH RECURSIVE`: una CTE que se referencia a sí misma para recorrer jerarquías y grafos —organigramas, listas de materiales, caminos—. Necesita siempre una condición de parada; sin ella el motor recorre hasta agotar la memoria.028
restricciónRegla declarada en el esquema que el motor impone siempre: `NOT NULL`, `UNIQUE`, `CHECK`, `PRIMARY KEY`, `FOREIGN KEY`. Su ventaja sobre la validación en la aplicación es que no depende de que alguien se acuerde.024
reunión externa`LEFT`, `RIGHT` o `FULL OUTER JOIN`: conserva las filas sin pareja y rellena con nulos. Cuidado con poner en el `WHERE` una condición sobre la tabla externa: la convierte de nuevo en interna.026
reunión interna`INNER JOIN`: devuelve solo los pares que casan. Las filas sin pareja desaparecen, y ese descarte silencioso es la causa más frecuente de informes con menos filas de las esperadas.026
semirreunionFiltrar una tabla por la existencia de una pareja, sin traer columnas de la otra ni multiplicar filas: `WHERE EXISTS (…)` o `IN (…)`. Es lo que casi siempre se quería cuando se escribió un `JOIN` seguido de `DISTINCT`.026
subconsulta correlacionadaSubconsulta que referencia una columna de la consulta externa y por tanto se evalúa en función de cada fila. Conceptualmente es un bucle; los optimizadores modernos suelen convertirla en una reunión, pero conviene comprobarlo en el plan y no suponerlo.028
tipo de datoLa declaración que fija qué valores acepta una columna y qué operaciones tienen sentido sobre ella. Es la primera línea de defensa del esquema y la más barata: lo que el tipo rechaza no hay que validarlo en ningún lenguaje de aplicación.024
UNKNOWNEl tercer valor de verdad de SQL, resultado de comparar con un nulo. No es verdadero ni falso: `NOT UNKNOWN` sigue siendo `UNKNOWN`, y un `WHERE` que se evalúa a `UNKNOWN` descarta la fila igual que si fuera falso.029
valor por defectoValor que el motor asigna cuando el `INSERT` no menciona la columna. Bien usado evita nulos accidentales; mal usado enmascara datos que faltaban de verdad y que convenía detectar.024

Fuentes usadas en esta parte

9 obras distintas sostienen lo que se afirma en estas 6 clases.

Otras partes