La idea
Una consulta se lee como una frase, pero la base de datos no la ejecuta en ese orden. Corre las
cláusulas en una secuencia fija: primero decide qué tabla mirar (FROM), luego qué filas conservar
(WHERE), y solo después construye las columnas que pediste (SELECT). Ordenar y recortar la lista
vienen al final.
SELECT en sí es simple. Por cada fila que sobrevivió produce una fila de salida, cuyos valores son
columnas que copias, expresiones que calculas, o ambas. Nunca agrega, quita ni reordena filas. De eso
se encargan las cláusulas que lo rodean.
Figura 1
El orden en que corre una consulta, no el orden en que la escribes
-
FROM productselige la tabla -
WHERE category = 'Furniture'conserva las filas que cumplen -
GROUP BY / HAVINGsolo si los escribiste -
SELECT unit_price - unit_cost AS marginconstruye las columnas -
ORDER BY margin DESCordena lo que queda -
LIMIT 3corta la lista
- Escribes
SELECTprimero, pero casi es lo último en ejecutarse. La base de datos empieza porFROM: ¿con qué tabla trabajamos? WHERErecorre las filas y conserva las que cumplen. En este punto las columnas de salida todavía no existen, solo las de la propia tabla.- Si la consulta tiene
GROUP BYyHAVING, se ejecutan aquí, entre el filtro y la salida. De ellos trata el capítulo 4. - Solo ahora
SELECTconstruye la salida, un valor por fila. El aliasmarginnace en este paso, por esoWHEREno puede verlo. ORDER BYcorre después deSELECT, así que puede ordenar por el aliasmargin. Al final llegaLIMIT, que se queda con las primeras filas de la lista ordenada.
Por qué existe
Imagina a una persona haciendo esto a mano con una lista de productos impresa. No escribiría primero la
columna de la respuesta. Buscaría la hoja, tacharía las filas que no importan y solo entonces calcularía
el margen de las que quedan. SQL es declarativo: describes el resultado y la base de datos decide cómo
obtenerlo. El orden fijo es lo que hace que tu descripción no sea ambigua. Sea cual sea el motor,
WHERE category = 'Furniture' significa "decide esto antes de calcular nada".
El orden también mantiene las consultas rápidas. Filtrar primero significa que la base de datos calcula
el margen solo de las filas que conservas y no de cada línea de pedido de la tienda. Y como FROM va
primero, todas las demás cláusulas pueden usar las columnas de la tabla sin decir de dónde vienen.
Visto así, una consulta es una pequeña línea de producción: cada cláusula toma lo que dejó la anterior y le pasa a la siguiente una tabla más pequeña o con otra forma. Nunca ves las tablas intermedias, pero siempre puedes reconstruirlas en tu cabeza, y así depuran los analistas.
Cómo funciona por dentro
Sigue esta consulta por la máquina:
SELECT name, unit_price - unit_cost AS margin
FROM products
WHERE category = 'Furniture'
ORDER BY margin DESC
LIMIT 3;
- FROM abre
products: todas las filas, todas las columnas. - WHERE prueba
category = 'Furniture'en cada fila y conserva las que pasan. - SELECT construye la salida. Por cada fila conservada copia
name, calcula la resta y llamamarginal resultado. - ORDER BY ordena las filas de salida. Corre después de
SELECT, así que el aliasmarginya existe. - LIMIT se queda con las tres primeras filas de la lista ordenada.
Las expresiones trabajan fila por fila
unit_price - unit_cost nunca mira la fila de arriba ni la de abajo. La resta se hace una vez por fila,
con los dos valores de esa fila. Por eso un SELECT con una expresión devuelve tantas filas como tiene la
tabla después de filtrar. No resume nada. Los resúmenes son el capítulo 4.
Figura 2
SELECT calcula un valor nuevo por fila, y el NULL se contagia
| product_id | name | unit_price | unit_cost |
|---|---|---|---|
| 101 | Vale Velvet Loveseat | 1659.00 | NULL |
| 102 | Alder Oak Coffee Table | 919.00 | 503.50 |
| 103 | Orchard Rattan Dining Chair | 789.00 | 429.87 |
| name | margin |
|---|---|
| Vale Velvet Loveseat | NULL |
| Alder Oak Coffee Table | 415.50 |
| Orchard Rattan Dining Chair | 359.13 |
- Tres filas de
products. El loveseat tiene precio pero no costo registrado: suunit_costesNULL, es decir, desconocido. SELECT name, unit_price - unit_cost AS margintoma una fila a la vez. Para la mesa de centro: 919.00 - 503.50 = 415.50.- Lo mismo con la silla de comedor: 789.00 - 429.87 = 359.13. La tabla conserva sus tres filas, solo cambia la forma.
- Para el loveseat la cuenta es 1659.00 menos un valor desconocido. La respuesta también es desconocida, así que
marginesNULL. La fila no se descarta y tampoco vale 1659.00.
NULL es un valor con el que no se puede calcular
Un NULL no es cero ni una cadena vacía. Marca "desconocido", y cualquier cuenta con un valor
desconocido da un valor desconocido. En Harbor & Pine algunos productos no tienen costo registrado,
así que su margen es NULL. Lo mismo pasa al pegar texto: 'Price: ' || NULL es NULL.
COALESCE(unit_cost, 0) cambia un NULL por un valor que tú eliges, y eso es una decisión de negocio.
Tómala a propósito.
Lo que puedes mostrar
Como SELECT construye la salida, puedes poner en ella cualquier cosa calculada a partir de la fila:
una columna, un cálculo, un valor redondeado, un texto armado a pedazos. Cada una se convierte en una
columna del resultado con el nombre que le des. Sin alias, la base de datos inventa un nombre como
unit_price - unit_cost, incómodo en una hoja de cálculo. Ponle nombre a cada columna calculada.
Leer una consulta en el orden en que corre
Cuando un resultado se ve mal, no leas desde arriba. Recorre la consulta como lo hace la máquina. Ejecuta
solo SELECT * FROM products WHERE category = 'Furniture' y mira las filas. ¿Coinciden con lo que
esperabas? Luego agrega la expresión y revisa un producto a mano: 919.00 - 503.50 debe dar 415.50. Luego
agrega el orden, luego el límite. Cada paso cambia una sola cosa, así que un número equivocado siempre
apunta a la cláusula que agregaste al final. Este hábito vale más que cualquier función que aprendas en
este capítulo.
Alias y DISTINCT
AS margin renombra una columna de salida. Hace legible el resultado y da a las cláusulas posteriores un
nombre corto, pero solo las cláusulas que corren después de SELECT pueden usarlo. ORDER BY puede. En
Postgres, MySQL y SQL Server WHERE no puede, y la solución es repetir la expresión. Algunos motores,
DuckDB incluido, lo toleran, así que no cuentes con ello. De igual modo, SELECT DISTINCT category se
ejecuta sobre las filas de salida después de SELECT: quita duplicados entre los valores que elegiste,
no entre las filas de la tabla.
Errores comunes
"SELECT corre primero, porque lo escribo primero"
Se escribe primero porque a las personas nos gusta empezar por la respuesta. La base de datos empieza
por FROM. Por eso no puedes usar un alias de columna en WHERE, y por eso SELECT no puede hacer
desaparecer filas. Cuando una consulta se comporta raro, léela en el orden en que corre, no en el que se
escribe.
"SELECT * es lo mismo que listar las columnas"
Hoy devuelve las mismas filas, pero cambia sin avisar el día que alguien agrega o reordena una columna, y
mueve datos que nunca usas. Nombrar las columnas además documenta para qué sirve la consulta. Usa *
para explorar y nombra las columnas en todo lo que guardes.
"Un costo faltante cuenta como cero"
La base de datos nunca lo decidirá por ti. NULL atraviesa la aritmética y sale como NULL. Si lo
tratas en silencio como cero, el loveseat parece un producto con un margen de $1,659 completos.
Pregúntate si desconocido de verdad significa cero y, si es así, dilo con COALESCE.
Ponte a prueba
Una pregunta. Al responderla, la lección queda marcada en el mapa; nunca bloquea un nivel.
No del todo. NULL no es cero. Significa desconocido, y 1659.00 menos un valor desconocido es desconocido. Usa COALESCE(unit_cost, 0) solo si decides que un costo faltante de verdad vale cero.Prueba otra respuesta.
No del todo. SELECT nunca elimina filas. Eso lo hacen WHERE (y HAVING, LIMIT). La fila se queda, con NULL en la columna del margen.Prueba otra respuesta.
Correcto. Cualquier cuenta que toque un NULL devuelve NULL. La fila se queda en el resultado con margin vacío.
No del todo. Eso sería inventar un número. La base de datos no puede saber el margen, así que informa NULL en lugar de 0.Prueba otra respuesta.
Lección hecha: ya está marcada en el mapa.