Atlasingeniería

Bases de datosSQLTema 1

SELECT, filtros y orden

La consulta más simple ya tiene todo lo que después complica: un orden de evaluación que no es el orden en que se escribe, nulos que no se comparan y un ORDER BY que no es gratis.

Para este tema conviene tener claro:Tablas, claves e integridad referencial

SELECT columnas FROM tabla WHERE condición es la primera línea de SQL que uno escribe y la que más veces va a escribir. Vale la pena entender qué hace el motor con ella, porque los tres errores más comunes de SQL ya están todos acá adentro.

Se escribe en un orden y se evalúa en otro

SQL se escribe en un orden y se evalúa en otro. Primero FROM, después WHERE, después GROUP BY y HAVING, recién ahí SELECT, y por último ORDER BY y LIMIT.

Eso explica dos cosas que desconciertan al principio. Un alias definido en el SELECT no se puede usar en el WHERE, porque cuando el filtro corre el alias todavía no existe. Y sí se puede usar en el ORDER BY, que corre después. No es un capricho del dialecto: es el orden lógico de evaluación.

Así se escribe la consulta: SELECT primero, FROM después, y el resto abajo.

1 / 6
La fila de abajo es el orden real. Buscá dónde cae el SELECT: todo lo que está a su izquierda corre antes de que el alias exista, y todo lo que está a su derecha lo puede usar. Esa única posición explica las dos rarezas.

Antes de seguir, predecí

Una consulta filtra por una columna con valores nulos usando distinto de «cancelado». ¿Salen las filas con nulo?

SELECT * y por qué no va en código

SELECT * es cómodo para explorar y mala idea en código. Trae columnas que no se usan —más red, más memoria—, se rompe silenciosamente cuando alguien agrega o reordena columnas, e impide que el motor resuelva la consulta leyendo sólo un índice.

Esa última es la más concreta: si un índice contiene todas las columnas pedidas, el motor responde sin tocar la tabla. Con *, nunca puede.

El nulo, fuente número uno de errores silenciosos

El nulo es la fuente de errores silenciosos número uno. No es un valor, es la ausencia de valor, y cualquier comparación con él da desconocido, que no es verdadero.

Por eso WHERE estado = NULL no devuelve nada nunca, y hay que escribir IS NULL. Y por eso WHERE estado <> 'activo' excluye las filas donde el estado es nulo, que casi nunca es lo que se quiso decir. Si esas filas tienen que entrar, hay que pedirlo explícitamente con un OR estado IS NULL.

Una función sobre la columna anula el índice

Los filtros tienen una regla práctica que vale más que cualquier truco: si la columna está envuelta en una función, el índice no se usa. WHERE YEAR(fecha) = 2026 recorre toda la tabla; WHERE fecha >= '2026-01-01' AND fecha < '2027-01-01' usa el índice y da lo mismo.

Lo mismo con LIKE '%texto%': el comodín al principio impide aprovechar el orden del índice, que está armado por prefijo. LIKE 'texto%' sí lo aprovecha. Para búsquedas dentro del texto hacen falta índices de texto completo, no LIKE.

ORDER BY no es gratis

ORDER BY no es gratis: si no hay un índice que ya entregue las filas en ese orden, el motor tiene que ordenar el resultado, y si no entra en memoria lo hace en disco.

Hay una trampa específica con LIMIT. Paginar con LIMIT 20 OFFSET 100000 obliga a producir y descartar cien mil filas: la página cien es mucho más cara que la primera. La alternativa es paginar por clave —“traeme los veinte siguientes a este valor”—, que usa el índice y cuesta lo mismo en cualquier página.

Sin ORDER BY no hay orden

Un detalle que muerde en producción: sin ORDER BY, el orden de las filas no está definido. Puede parecer estable durante meses y cambiar cuando el motor elige otro plan o los datos se reorganizan.

Y si el ORDER BY no desempata —ordenar por fecha cuando hay muchas filas con la misma fecha—, la paginación puede repetir o saltear filas entre páginas. La regla es incluir siempre una columna única al final del orden.

La misma consulta, bien y mal

-- Mal: trae columnas que no se usan y aplica una función sobre la columna filtrada
SELECT *
FROM invoices
WHERE YEAR(issued_at) = 2026
ORDER BY issued_at DESC;

-- Bien: pide lo que necesita y deja la columna sin tocar, para que el índice sirva
SELECT id, customer_id, total, issued_at
FROM invoices
WHERE issued_at >= '2026-01-01'
  AND issued_at <  '2027-01-01'
ORDER BY issued_at DESC
LIMIT 50;

Los dos filtros piden lo mismo y sólo el segundo puede usar un índice sobre issued_at. Aplicar YEAR() sobre la columna produce un valor que no está indexado, así que el motor tiene que calcularlo para cada fila de la tabla. Convertir un filtro sobre una función en un filtro por rango es la reescritura que más veces salva una consulta lenta.

El LIMIT sin ORDER BY merece una advertencia aparte: sin orden explícito, qué 50 filas devuelve el motor no está definido, y puede cambiar entre ejecuciones o entre versiones. Un LIMIT sin ORDER BY es una consulta no determinista aunque parezca estable.

Lo que decide si una consulta es rápida

Escrito asíEl motor puede usar el índicePor qué
fecha >= 'x' AND fecha < 'y'el rango se resuelve bajando por el árbol
YEAR(fecha) = 2026noel valor calculado no está indexado
nombre LIKE 'ana%'hay prefijo por donde bajar
nombre LIKE '%ana'nosin prefijo, hay que mirar todo
id IN (1, 2, 3)son tres búsquedas por índice
estado != 5casi nuncala negación abarca casi toda la tabla
Las seis filas se resumen en una regla: el índice sirve mientras la columna aparezca tal cual está guardada y el filtro sea un prefijo o un rango.

Cierre

El orden de evaluación explica dónde vale cada alias; el nulo exige IS NULL y cuidado con las desigualdades; envolver la columna en una función mata el índice; y sin ORDER BY con desempate no hay orden garantizado ni paginación confiable.

Autoevaluación

¿Lo entendiste?

¿Por qué un alias del SELECT no se puede usar en el WHERE, pero sí en el ORDER BY?
WHERE estado = NULL no devuelve nada. ¿Por qué?
¿Cuál es el argumento más concreto contra SELECT * en código?
Una consulta con LIMIT y sin ORDER BY, ¿qué devuelve?