Tema 7: SELECT, WHERE, ORDER BY: pedirle datos a la base de datos sin llorar
Identificador: PF-007
XP: 150
Insignia: 🥈 Plata — "Primer query"
Tier: Aprendiz
Tiempo estimado: 90-120 minutos (lectura + LAB)
Pre-requisitos: PF-001 a PF-006
🖼️ Imagen de inicio del tema

[Ilustración cartoon a color: Moka personificada como tlacuilo mexica entrega pergaminos con querys a Sofía, quien está en un calmécac decorado con murales de productos mesoamericanos (cacao, maíz, obsidiana). Don Carlos le señala con un dedo la cláusula WHERE mientras Citlalli, a un lado, anota en un codex los nombres de los filtros. El fondo muestra el mercado de Tlatelolco con clientes y productos.]
Pie de imagen: "SELECT es pedirle al calmécac que te entregue solo los registros que necesitas, no todo el archivo."
🎯 En este tema aprenderás
👥 Los personajes de tu viaje
(Sofía, Don Carlos, Moka, Citlalli, Nube, Robotillo — los mismos del Cap 1)
🏛️ Visión mexica — Por Citlalli
Citlalli aporta: "Cuando un calpixqui en 1519 necesitaba saber 'cuántas cargas de cacao llegaron de Suchimilco este año', no hojeaba todo el archivo del calmécac. Le decía al tlacuilo: 'dame los registros de cacao de Suchimilco del año actual'. El tlacuilo filtraba y entregaba.
Eso es exactamente lo que hace `SELECT ... WHERE`. La cláusula WHERE es tu 'filtro del tlacuilo': le dices qué registros quieres, y la base de datos te entrega solo esos. Sin WHERE, te traes toda la tabla — igual que si el tlacuilo te entregara todo el archivo del calmécac para que tú buscaras manualmente. Eso era impensable: en un imperio con 371 pueblos tributarios, buscar manualmente habría tomado días.
La analogía moderna: si tienes 50 mil clientes y quieres 'los de Monterrey', el WHERE ciudad = 'Monterrey' le dice a la base de datos 'solo dame esos'. Sin WHERE, te traes los 50 mil y luego filtras en tu aplicación. Eso es lento, ineficiente, y en producción con muchos datos, simplemente no funciona."
📚 Bloque 1: Tu primer SELECT real
En el Tema 1 ya ejecutaste tu primer query. Ahora la vamos a desarmar pieza por pieza.
SELECT nombre, email FROM clientes;Desglose:
- `SELECT` — la palabra clave que dice "trae datos de la base".
- `nombre, email` — las COLUMNAS que quieres ver. Si pones `` en lugar de nombres, te trae TODAS las columnas.
- `FROM clientes` — la TABLA de donde sacar los datos.
- `;` — el punto y coma que cierra la sentencia. En phpMyAdmin es opcional; en la línea de comandos es obligatorio.
SELECT FROM clientes;El asterisco `` significa "todas las columnas". Es cómodo para explorar, pero en producción es mejor listar las columnas que necesitas: hace la query más rápida y el código más claro.
Don Carlos aclara: Regla del DBA profesional: nunca uses `SELECT ` en código de producción. ¿Por qué? Tres razones:
📚 Bloque 2: WHERE — el filtro del tlacuilo
`WHERE` es la cláusula que filtra filas. Sin WHERE, te llevas todo. Con WHERE, pones condiciones.
Operadores de comparación:
| Operador | Significado | Ejemplo | |---|---|---| | `=` | Igual a | `WHERE ciudad = 'Monterrey'` | | `<>` o `!=` | Diferente de | `WHERE pais <> 'México'` | | `>` | Mayor que | `WHERE precio > 1000` | | `<` | Menor que | `WHERE stock < 10` | | `>=` | Mayor o igual | `WHERE fecha >= '2026-01-01'` | | `<=` | Menor o igual | `WHERE calificacion <= 3` |
Ejemplo de la Tienda Tlalli:
-- Dame los productos de la categoría "Laptops" (id=14) con precio menor a $15,000
SELECT nombre, precio
FROM productos
WHERE categoria_id = 14 AND precio < 15000;Operadores lógicos:
- `AND` — ambas condiciones deben ser verdaderas.
- `OR` — al menos una condición debe ser verdadera.
- `NOT` — invierte la condición.
-- Productos que cuestan menos de $500 O más de $20,000
SELECT nombre, precio
FROM productos
WHERE precio < 500 OR precio > 20000;-- Clientes que NO son de Ciudad de México
SELECT nombre, ciudad
FROM clientes
WHERE NOT ciudad = 'Ciudad de México';
Sofía pregunta: ¿Cómo filtro texto que contiene una palabra?
-- Productos cuyo nombre contiene "Laptop"
SELECT nombre, precio
FROM productos
WHERE nombre LIKE '%Laptop%';El operador `LIKE` con `%` significa "cualquier secuencia de caracteres". `'%Laptop%'` matchea "Laptop HP", "MacBook Laptop", "Laptops Lenovo", etc.
Moka: "Ojo: `LIKE` distingue MAYÚSCULAS de minúsculas en MySQL solo si la collation es case-sensitive. En la mayoría de las instalaciones no, pero siempre prueba. Si quieres ser explícito, usa `LIKE BINARY` en MySQL o `ILIKE` en PostgreSQL."
📚 Bloque 3: ORDER BY — ordenando los resultados
Por defecto, las bases de datos devuelven los resultados en el orden en que los encontraron (que no es útil). `ORDER BY` te permite ordenarlos por cualquier columna.
-- Productos del más barato al más caro
SELECT nombre, precio
FROM productos
ORDER BY precio ASC; -- ASC = ascendente (menor a mayor)-- Productos del más caro al más barato
SELECT nombre, precio
FROM productos
ORDER BY precio DESC; -- DESC = descendente (mayor a menor)
Don Carlos: Ordenar por más de una columna es legal y útil:
-- Pedidos más recientes primero, y dentro de la misma fecha, los de mayor total primero
SELECT id, fecha_pedido, total
FROM pedidos
ORDER BY fecha_pedido DESC, total DESC;
Citlalli: "Para los mexicas, el ORDEN era crucial: los tributos se entregaban primero al huey tlatoani (el emperador), luego a los nobles, luego al pueblo. El 'ORDER BY jerarquía DESC' era su forma natural de organizar cualquier registro."
📚 Bloque 4: LIMIT — no te traigas todo
Cuando exploras una tabla con 50 mil filas, NO quieres ver 50 mil filas en tu pantalla. `LIMIT` te trae solo las primeras N.
-- Los 10 productos más caros
SELECT nombre, precio
FROM productos
ORDER BY precio DESC
LIMIT 10;Diferencias entre motores:
- MySQL / PostgreSQL: `LIMIT n` (al final).
- SQL Server: `TOP n` (al inicio, después de SELECT).
- Oracle: `FETCH FIRST n ROWS ONLY` (estándar moderno).
-- Equivalentes
-- MySQL / PostgreSQL:
SELECT nombre FROM productos ORDER BY precio DESC LIMIT 10;-- SQL Server:
SELECT TOP 10 nombre FROM productos ORDER BY precio DESC;
-- Oracle / estándar SQL:
SELECT nombre FROM productos ORDER BY precio DESC FETCH FIRST 10 ROWS ONLY;
Sofía pregunta: ¿Por qué LIMIT va al final y no donde lo pone SQL Server?
Don Carlos: Es cuestión de diseño del lenguaje. En MySQL/PostgreSQL, el orden de la query es: SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT. SQL Server invierte la posición. Es histórico: cada motor evolucionó con su propia sintaxis. La tendencia moderna es la de MySQL/PostgreSQL porque es más legible (lees en el mismo orden que procesa la base).
📚 Bloque 5: Combinando todo — el patrón de query útil
El 80% de las queries de un DBA junior siguen este patrón:
SELECT columna1, columna2
FROM tabla
WHERE condición
ORDER BY columna [ASC | DESC]
LIMIT n;Ejemplo real de Tienda Tlalli:
-- Los 5 clientes más recientes de Monterrey que siguen activos
SELECT nombre, email, fecha_registro
FROM clientes
WHERE ciudad = 'Monterrey' AND activo = 1
ORDER BY fecha_registro DESC
LIMIT 5;Moka: "Esa query es tu navaja suiza. La usarás 100 veces al día. Memorízala."
📚 Bloque 6: Errores comunes con WHERE
Error 1 — Comparar NULL con =:
-- MAL: esto nunca es verdadero
SELECT FROM clientes WHERE telefono = NULL;-- BIEN: usa IS NULL
SELECT FROM clientes WHERE telefono IS NULL;
`NULL` significa "ausencia de valor", no "cadena vacía". Compararlo con `=` nunca funciona. Usa `IS NULL` o `IS NOT NULL`.
Error 2 — Comillas en valores numéricos:
-- MAL: precio es número, sin comillas
SELECT FROM productos WHERE precio = '1500';-- BIEN
SELECT FROM productos WHERE precio = 1500;
Las comillas son para strings (`'texto'`) y fechas (`'2026-01-01'`). Los números van sin comillas. Si pones comillas, MySQL puede aceptar la comparación pero con advertencia; PostgreSQL puede fallar.
Error 3 — Case sensitivity:
-- Esto puede no encontrar 'sofia.hdz@example.com' si la collation es case-sensitive
SELECT FROM clientes WHERE email = 'Sofia.HDZ@example.com';-- Para búsqueda case-insensitive en MySQL:
SELECT FROM clientes WHERE LOWER(email) = LOWER('Sofia.HDZ@example.com');
-- En PostgreSQL:
SELECT FROM clientes WHERE email ILIKE 'sofia.hdz@example.com';
Moka: "El email es case-insensitive por convención, pero las collationes varían. Si vas a buscar emails en una tabla grande, agrega un índice sobre LOWER(email) para mantener performance."
🤖 AI Mission — "Pregúntale, no le pidas que lo haga"
Misión 7.1: Pídele a la IA:
"Tengo una tabla 'clientes' con 50 mil filas. Quiero los 100 clientes más recientes de la Ciudad de México que estén activos. Escribe la query y luego explícame línea por línea por qué es eficiente."
Misión 7.2: Pídele a la IA:
"¿Por qué SELECT FROM tabla_grande es una mala práctica? Dame 3 razones con ejemplos concretos del mundo real."
⚠️ Errores típicos del principiante
🧪 LAB 7.1 — "Las 10 queries de un DBA en su primer día"
Objetivo: ejecutar 10 queries que te van a servir SIEMPRE que te conectes a una base nueva. Practica el patrón `SELECT ... WHERE ... ORDER BY ... LIMIT ...`.
Materiales: DBeaver conectado a MySQL o PostgreSQL, base de datos `tienda_tlalli` cargada.
Instrucciones — escribe cada query, ejecútala y anota el resultado en tu cuaderno:
Query 1: los 10 productos más baratos.
SELECT nombre, precio FROM productos ORDER BY precio ASC LIMIT 10;Query 2: los 5 productos más caros de la categoría "Laptops" (id=14).
SELECT nombre, precio FROM productos WHERE categoria_id = 14 ORDER BY precio DESC LIMIT 5;Query 3: todos los clientes de Guadalajara ordenados por fecha de registro (más recientes primero).
SELECT nombre, email, fecha_registro FROM clientes WHERE ciudad = 'Guadalajara' ORDER BY fecha_registro DESC;Query 4: productos con stock bajo (menos de 30 unidades) ordenados alfabéticamente.
SELECT nombre, stock FROM productos WHERE stock < 30 ORDER BY nombre ASC;Query 5: las 3 reseñas con peor calificación.
SELECT producto_id, cliente_id, calificacion, comentario FROM resenas ORDER BY calificacion ASC LIMIT 3;Query 6: pedidos del estado "pendiente" en orden cronológico.
SELECT id, cliente_id, fecha_pedido, total FROM pedidos WHERE estado = 'pendiente' ORDER BY fecha_pedido ASC;Query 7: clientes cuyo email contiene "gmail" o "yahoo".
SELECT nombre, email FROM clientes WHERE email LIKE '%gmail%' OR email LIKE '%yahoo%' ORDER BY email;Query 8: productos con nombre que empieza con "M".
SELECT nombre, precio FROM productos WHERE nombre LIKE 'M%' ORDER BY nombre;Query 9: pedidos con total entre $5,000 y $10,000 (inclusive).
SELECT id, cliente_id, total FROM pedidos WHERE total BETWEEN 5000 AND 10000 ORDER BY total;Query 10: los 3 clientes con fecha de registro más antigua que siguen activos.
SELECT nombre, fecha_registro FROM clientes WHERE activo = 1 ORDER BY fecha_registro ASC LIMIT 3;Verificación: las 10 queries deben ejecutarse sin error. Cada resultado debe tener sentido (cantidades razonables, fechas coherentes, precios correctos).
🧪 LAB 7.2 — "Tu primera query de análisis de negocio"
Objetivo: simular una pregunta real de un negocio y construir la query que la responde.
Contexto: Tienda Tlalli quiere saber cuáles son los 5 productos más populares entre los clientes que viven en Monterrey. Los productos populares son los que tienen más pedidos.
Query paso a paso:
Paso 1 — Encuentra los pedidos de clientes de Monterrey:
SELECT id, cliente_id FROM pedidos WHERE cliente_id IN (
SELECT id FROM clientes WHERE ciudad = 'Monterrey'
);Paso 2 — Encuentra los items de esos pedidos:
SELECT producto_id, COUNT() AS veces_pedido
FROM pedido_items
WHERE pedido_id IN (
SELECT id FROM pedidos WHERE cliente_id IN (
SELECT id FROM clientes WHERE ciudad = 'Monterrey'
)
)
GROUP BY producto_id
ORDER BY veces_pedido DESC
LIMIT 5;Paso 3 — Agrega el nombre del producto:
SELECT p.nombre, COUNT() AS veces_pedido
FROM pedido_items pi
JOIN productos p ON p.id = pi.producto_id
WHERE pi.pedido_id IN (
SELECT id FROM pedidos WHERE cliente_id IN (
SELECT id FROM clientes WHERE ciudad = 'Monterrey'
)
)
GROUP BY p.nombre
ORDER BY veces_pedido DESC
LIMIT 5;(No te preocupes si no entiendes los `JOIN` y `GROUP BY` ahora — los verás en los Temas 9 y 10. Lo importante aquí es ver la ESTRUCTURA de una query de negocio real).
📝 Quest 7 — Preguntas de opción múltiple
Pregunta 1. ¿Qué hace la cláusula WHERE en una query SQL?
A) Ordena los resultados. B) Filtra las filas según una condición. C) Especifica las columnas a retornar. D) Cuenta el número de filas.
Pregunta 2. ¿Cuál es la diferencia entre `=` y `IS NULL`?
A) Son sinónimos, no hay diferencia. B) `=` compara valores; `IS NULL` verifica ausencia de valor. C) `IS NULL` es para strings; `=` es para números. D) `=` no funciona con NULL; `IS NULL` sí.
Pregunta 3. ¿Cómo ordenas los resultados de mayor a menor precio?
A) `ORDER BY precio` B) `ORDER BY precio ASC` C) `ORDER BY precio DESC` D) `SORT BY precio DESC`
Pregunta 4. ¿Qué hace `LIMIT 10` en una query?
A) Cuenta el número de filas. B) Limita el resultado a las primeras 10 filas. C) Divide el resultado en 10 páginas. D) Solo funciona en MySQL.
Pregunta 5. ¿Cuál de estas queries busca productos con precio mayor a $5,000?
A) `SELECT FROM productos WHERE precio > 5000;` B) `SELECT FROM productos WHERE precio >= 5000;` C) `SELECT FROM productos HAVING precio > 5000;` D) `SELECT FROM productos WHERE precio > '5000';`
Pregunta 6. ¿Por qué NO es buena práctica usar `SELECT ` en producción?
A) Porque no funciona en todas las bases de datos. B) Porque devuelve todas las columnas, lo cual es ineficiente y propenso a errores si la tabla cambia. C) Porque es muy lento en consultas simples. D) Porque requiere permisos especiales.
Pregunta 7. En MySQL, ¿cuál es la sintaxis correcta para buscar clientes con email que termine en "@example.com"?
A) `WHERE email LIKE '@example.com%'` B) `WHERE email LIKE '%@example.com'` C) `WHERE email = '@example.com'` D) `WHERE email ENDS '@example.com'`
Pregunta 8. ¿Qué operador se usa para "diferente de" en SQL estándar?
A) `!=` B) `<>` C) Ambos `!=` y `<>` son válidos D) `!==`
Pregunta 9. ¿Cuál es el orden correcto de las cláusulas en una query SELECT?
A) SELECT, FROM, WHERE, ORDER BY, LIMIT B) SELECT, WHERE, FROM, ORDER BY, LIMIT C) SELECT, FROM, ORDER BY, WHERE, LIMIT D) No importa el orden.
Pregunta 10. Si ejecutas `SELECT FROM productos WHERE nombre = 'Laptop'` y no obtienes resultados, pero sabes que hay laptops, ¿cuál es la causa más probable?
A) La tabla está vacía. B) La columna nombre tiene collation case-sensitive y escribiste mal la mayúscula. C) WHERE no funciona sin ORDER BY. D) El producto fue eliminado.
📝 Quest 7 — Preguntas abiertas
Pregunta 11. Explica con tus palabras la diferencia entre `ORDER BY` y `WHERE`. ¿Se pueden usar juntos? Da un ejemplo.
Pregunta 12. ¿Por qué `SELECT FROM productos WHERE stock = 0` puede ser una query importante en producción? ¿Qué decisión de negocio tomarías con esa información?
Pregunta 13. Diseña una query para resolver este problema: "El equipo de marketing quiere los 20 productos más vendidos en 2025, ordenados por cantidad vendida de mayor a menor, mostrando el nombre del producto y el total de unidades vendidas". No la ejecutes (no tienes la sintaxis completa aún), pero escribe el SQL aproximado.
✅ Respuestas modelo — Quest 7
Pregunta 1: B (filtra filas). Pregunta 2: B (`=` compara valores, `IS NULL` verifica ausencia). D es trampa: `=` SÍ funciona con NULL pero el resultado es NULL/falso. Pregunta 3: C (DESC = descendente). Pregunta 4: B (limita a N filas). Pregunta 5: A (con `>`). B es "mayor o igual", no es lo mismo. Pregunta 6: B (trae todas las columnas, ineficiente, propenso a errores). Pregunta 7: B (`%@example.com` — `%` al inicio, no al final). Pregunta 8: C (ambos son válidos en SQL moderno). Pregunta 9: A (SELECT, FROM, WHERE, ORDER BY, LIMIT). Pregunta 10: B (case-sensitivity es la causa más común de "no encuentra" en queries).
🏛️ Tras las huellas — Por Citlalli
"El tlacuilo y el DBA: filtrar y ordenar como práctica milenaria"
El tlacuilo mexica no solo escribía glifos: tenía que ENCONTRARLOS cuando se los pedían. Si el calpixqui llegaba al calmécac y decía "dame los registros de tributos de cacao de Suchimilco del año pasado", el tlacuilo tenía que:
1. Ir al anaquel correcto (FROM). 2. Sacar el codex del año correcto (WHERE con filtro de fecha). 3. Buscar las páginas con el glifo de Suchimilco (WHERE con filtro de altépetl). 4. Identificar las cargas de cacao (WHERE con filtro de producto). 5. Ordenarlas por fecha o por cantidad (ORDER BY). 6. Entregar solo las que el calpixqui necesitaba ver (LIMIT).
Esas seis operaciones son EXACTAMENTE las mismas que hace tu query SELECT. La interfaz cambió (de glifos a columnas, de calmécac a base de datos), pero el flujo mental es idéntico: ir a la fuente, filtrar, ordenar, entregar.
Tres lecciones de los tlacuilos para tu trabajo de DBA:
1. Conocer el "anaquel" (FROM): el tlacuilo sabía dónde estaba cada codex. Tú necesitas saber qué tablas existen y qué información tienen. Por eso el Tema 17 (modelado ER) es fundamental.
2. Dominar el "filtro" (WHERE): el tlacuilo usaba combinaciones de glifos para filtrar. Tú usas WHERE con AND, OR, NOT, LIKE, BETWEEN. Practica esto hasta que sea automático.
3. Cuidar el "orden" (ORDER BY): el tlacuilo sabía que algunos registros eran más importantes que otros. Tu ORDER BY debe reflejar las prioridades del negocio.
En tu cuaderno, anota: ¿qué analogía del tlacuilo te pareció más útil para entender una query SELECT? ¿Se te ocurre otra operación del calmécac que se parezca a una cláusula SQL?*
📓 Cierre del cuaderno — 4 líneas para escribir a mano
1. La idea más importante de este tema para mí fue:>
2. Una pregunta que me quedó dando vueltas:>
3. La query que más me costó entender fue:>
4. Una conexión con algo que ya sabía:
🚀 ¿Qué sigue?
En el Tema 8 — INSERT, UPDATE, DELETE: el mantra CRUD con cuidado vamos a ver las otras 3 operaciones del CRUD. SELECT es lectura; INSERT, UPDATE, DELETE son escritura. Y aquí es donde un DBA junior aprende a tenerle RESPETO al botón "ejecutar" — un DROP sin WHERE y se te cae la producción.