Lección 8 de 16 · Nivel 2: Intermedio
Introducción a SQL: SELECT, WHERE y ORDER BY
- ⏱ 13 min de lectura
- ❓ 4 preguntas de repaso
- ✍️ Ejercicio con solución
- 📥 Plantilla descargable
En esta lección
- Entender qué es SQL, qué es una base de datos relacional y por qué todo analista lo necesita.
- Crear una base de datos de práctica con las ventas de Nexo y abrirla en una herramienta gratuita.
- Escribir consultas con SELECT, FROM, DISTINCT y alias.
- Filtrar filas con WHERE: comparaciones, BETWEEN, IN, LIKE e IS NULL.
- Ordenar y limitar resultados con ORDER BY y LIMIT, y evitar la trampa de AND y OR.
Con estos dos archivos crearás la base de datos nexo.db (instrucciones abajo).
- ventas-practica-analisis.xlsx · hojas Ventas_limpias y Regiones
- ventas-practica-analisis.csv · datos sin limpiar
SQL (Structured Query Language) es el lenguaje para pedirle datos a una base de datos. En lugar de abrir un archivo y filtrar con el mouse, escribes una instrucción como «dame los pedidos de Lima por la web, del más caro al más barato» y la base de datos te devuelve el resultado, aunque la tabla tenga millones de filas. Casi todas las empresas guardan sus ventas, clientes e inventarios en bases de datos, por eso SQL aparece en la gran mayoría de ofertas de analista. La buena noticia: con media docena de palabras clave ya puedes hacer consultas útiles.
Qué es una base de datos relacional
Una base de datos relacional guarda la información en tablas (como hojas de Excel con reglas estrictas): cada columna tiene un tipo de dato fijo y cada fila es un registro. Las tablas se relacionan entre sí por columnas comunes; por ejemplo, la tabla ventas y la tabla regiones comparten la columna region. Los sistemas más conocidos son PostgreSQL, MySQL, SQL Server, Oracle, SQLite y, en la nube, BigQuery o Snowflake. Cada uno tiene pequeñas diferencias, pero lo que aprenderás aquí funciona en todos.
Prepara tu base de datos de práctica
La forma más sencilla y fiable de crearla es con Python, aunque todavía no sepas programar: copia este código en Google Colab (gratis, en el navegador) o en tu computadora, con los dos archivos del curso en la misma carpeta, y ejecútalo:
import sqlite3
import pandas as pd
archivo = "ventas-practica-analisis.xlsx"
con = sqlite3.connect("nexo.db")
ventas = pd.read_excel(archivo, sheet_name="Ventas_limpias")
ventas["fecha"] = ventas["fecha"].dt.strftime("%Y-%m-%d")
ventas.to_sql("ventas", con, index=False, if_exists="replace")
pd.read_excel(archivo, sheet_name="Regiones").to_sql(
"regiones", con, index=False, if_exists="replace")
pd.read_csv("ventas-practica-analisis.csv").to_sql(
"ventas_sin_limpiar", con, index=False, if_exists="replace")
print(pd.read_sql("SELECT COUNT(*) AS filas FROM ventas", con)) # 197
con.close()
Se crea el archivo nexo.db. Ábrelo con DB Browser for SQLite (programa gratuito), ve a la pestaña «Ejecutar SQL» y ya puedes escribir consultas. Las fechas se guardan como texto en formato AAAA-MM-DD, que se ordena y compara correctamente. Si quieres entender el código, el curso de Python lo explica en la lección de bases de datos con SQLite y SQL.
Anatomía de una consulta
Una consulta básica se escribe siempre en este orden. Solo SELECT y FROM son obligatorias:
- SELECT: qué columnas
- FROM: de qué tabla
- WHERE: qué filas
- ORDER BY: en qué orden
- LIMIT: cuántas filas
SELECT y FROM: elige columnas
La consulta más simple muestra todas las columnas (*) de una tabla. Con LIMIT ves solo las primeras filas, ideal para conocer una tabla nueva sin cargarla entera:
SELECT *
FROM ventas
LIMIT 5;
| id_pedido | fecha | region | producto | categoria | canal | unidades | precio_unitario | importe |
|---|---|---|---|---|---|---|---|---|
| P-0001 | 2025-01-03 | Antioquia | Silla ergonómica | Oficina | Web | 1 | 189.0 | 189.0 |
| P-0002 | 2025-01-03 | Lima | Mouse inalámbrico | Accesorios | Web | 7 | 17.5 | 122.5 |
| P-0003 | 2025-01-04 | Lima | Teclado mecánico | Accesorios | Marketplace | 6 | 54.9 | 329.4 |
| P-0004 | 2025-01-04 | Bogotá | Mochila para laptop | Accesorios | Marketplace | 1 | 39.9 | 39.9 |
| P-0005 | 2025-01-09 | Ciudad de México | Audífonos Bluetooth | Audio | Tienda física | 1 | 44.9 | 44.9 |
Para ver valores sin repetir, usa DISTINCT. Esta consulta devuelve las 4 categorías, en orden alfabético: Accesorios, Audio, Cómputo y Oficina.
SELECT DISTINCT categoria
FROM ventas
ORDER BY categoria;
También puedes crear columnas calculadas y ponerles nombre con AS (alias). Esta consulta recalcula el importe de los pedidos con 8 unidades o más:
SELECT id_pedido, producto, unidades, precio_unitario,
unidades * precio_unitario AS importe_calculado
FROM ventas
WHERE unidades >= 8
ORDER BY unidades DESC, id_pedido;
| id_pedido | producto | unidades | precio_unitario | importe_calculado |
|---|---|---|---|---|
| P-0133 | Mouse inalámbrico | 12 | 17.5 | 210.0 |
| P-0120 | Mouse inalámbrico | 10 | 17.5 | 175.0 |
| P-0088 | Mouse inalámbrico | 9 | 17.5 | 157.5 |
| P-0076 | Mouse inalámbrico | 8 | 17.5 | 140.0 |
| P-0102 | Mouse inalámbrico | 8 | 17.5 | 140.0 |
| P-0170 | Mouse inalámbrico | 8 | 17.5 | 140.0 |
Curioso: todos los pedidos de 8 unidades o más son de mouse. Los compradores de volumen buscan productos baratos, una pista que retomarás al estudiar correlaciones.
WHERE: filtra las filas
WHERE deja pasar solo las filas que cumplen la condición. Estos son los operadores que más usarás:
| Operador | Ejemplo | Significa |
|---|---|---|
=, <> | canal = 'Web' | Igual, distinto |
>, >=, <, <= | importe >= 600 | Comparaciones numéricas o de fechas |
BETWEEN … AND … | fecha BETWEEN '2025-12-24' AND '2025-12-31' | Entre dos valores, ambos incluidos |
IN (…) | region IN ('Lima', 'Arequipa') | Cualquiera de la lista |
LIKE | producto LIKE 'Laptop%' | Patrón de texto: % = cualquier cosa |
IS NULL, IS NOT NULL | importe IS NULL | Valor vacío o no vacío |
AND, OR, NOT | region = 'Lima' AND canal = 'Web' | Combinar condiciones |
Los textos y las fechas van entre comillas simples. Una consulta típica combina WHERE, ORDER BY y LIMIT para responder una pregunta tipo «top 5»: ¿cuáles fueron los cinco pedidos web más grandes de Lima?
SELECT id_pedido, fecha, producto, importe
FROM ventas
WHERE region = 'Lima' AND canal = 'Web'
ORDER BY importe DESC
LIMIT 5;
| id_pedido | fecha | producto | importe |
|---|---|---|---|
| P-0098 | 2025-08-06 | Laptop 14 pulgadas | 1298.0 |
| P-0115 | 2025-09-02 | Tablet 10 pulgadas | 837.0 |
| P-0145 | 2025-11-07 | Monitor 24 pulgadas | 319.8 |
| P-0153 | 2025-11-13 | Parlante portátil | 239.6 |
| P-0131 | 2025-10-05 | Teclado mecánico | 219.6 |
El resultado de la consulta como gráfico: cada barra, proporcional al pedido más grande (1.298 USD). Dos pedidos concentran la mayor parte del valor del top 5.
Dato curioso: P-0153 y P-0131 venían escritos como «lima» y «Lima » en la tabla sin limpiar. En ventas_sin_limpiar, esta misma consulta no los encontraría, porque en SQLite (y en PostgreSQL) el = distingue mayúsculas y espacios.
BETWEEN, IN y LIKE en acción
¿Qué laptops se vendieron en las regiones de Perú? Usamos LIKE para el producto e IN para la lista de regiones:
SELECT id_pedido, fecha, region, unidades, importe
FROM ventas
WHERE producto LIKE 'Laptop%'
AND region IN ('Lima', 'Arequipa', 'La Libertad')
ORDER BY fecha;
| id_pedido | fecha | region | unidades | importe |
|---|---|---|---|---|
| P-0092 | 2025-07-25 | Arequipa | 1 | 649.0 |
| P-0098 | 2025-08-06 | Lima | 2 | 1298.0 |
| P-0136 | 2025-10-11 | Arequipa | 1 | 649.0 |
| P-0162 | 2025-11-25 | Arequipa | 2 | 1298.0 |
| P-0168 | 2025-12-02 | Lima | 1 | 649.0 |
Arequipa compró más laptops que Lima, y todas en el segundo semestre. Para fechas, BETWEEN incluye los dos extremos: WHERE fecha BETWEEN '2025-12-24' AND '2025-12-31' devuelve los 5 pedidos de la última semana del año (P-0193 a P-0197).
1. ¿Qué consulta devuelve los pedidos de Bogotá o Antioquia con importe mayor a 300?
IN compara contra una lista. La primera opción está mal escrita (cada comparación necesita su columna: region = 'Antioquia') y la tercera busca una región llamada literalmente «Bogotá, Antioquia», que no existe.
Buscar vacíos: IS NULL
En SQL, un valor vacío es NULL y no se compara con =. En la tabla sin limpiar:
SELECT id_pedido, region, producto, unidades, precio_unitario, importe
FROM ventas_sin_limpiar
WHERE importe IS NULL;
| id_pedido | region | producto | unidades | precio_unitario | importe |
|---|---|---|---|---|---|
| P-0017 | Bogotá | Monitor 24 pulgadas | 1 | 159.9 | NULL |
| P-0035 | Antioquia | Mochila para laptop | 1 | 39.9 | NULL |
| P-0068 | Lima | Audífonos Bluetooth | 1 | 44.9 | NULL |
| P-0099 | Ciudad de México | Lámpara LED de escritorio | 3 | 27.5 | NULL |
Son los 4 importes vacíos que corregiste en la lección 5. Si escribes WHERE importe = NULL, la consulta devuelve 0 filas sin dar error: NULL significa «desconocido», y nada es igual a algo desconocido.
La trampa de AND y OR
AND se evalúa antes que OR, igual que la multiplicación antes que la suma. Mira la diferencia entre estas dos consultas que parecen iguales:
-- Sin paréntesis: devuelve 101 filas
SELECT COUNT(*) FROM ventas
WHERE canal = 'Web' OR canal = 'Marketplace' AND importe > 500;
-- Con paréntesis: devuelve 14 filas
SELECT COUNT(*) FROM ventas
WHERE (canal = 'Web' OR canal = 'Marketplace') AND importe > 500;
La primera se lee como «todos los pedidos web (94), más los de Marketplace mayores a 500 (7)». La segunda es lo que probablemente querías: pedidos de cualquiera de los dos canales que superan 500. COUNT(*) cuenta filas; lo verás a fondo en la próxima lección. Regla práctica: siempre que mezcles AND y OR, usa paréntesis.
2. ¿Por qué la consulta sin paréntesis devuelve 101 filas?
Sin paréntesis, la condición equivale a canal = 'Web' OR (canal = 'Marketplace' AND importe > 500): 94 pedidos web + 7 de Marketplace = 101.
En qué orden «piensa» la base de datos
Escribes SELECT primero, pero la base de datos lo procesa en otro orden: FROM → WHERE → SELECT → ORDER BY → LIMIT. Primero toma la tabla, luego filtra filas, después elige y calcula columnas, ordena y, al final, corta. Por eso puedes ordenar por un alias creado en SELECT (ORDER BY importe_calculado), pero en muchos sistemas no puedes usar ese alias dentro de WHERE.
Errores comunes (y qué hacer en su lugar)
Escribir textos con comillas dobles: WHERE canal = "Web".
Usa comillas simples para valores: 'Web'. Las dobles, en el estándar, son para nombres de columnas.
Buscar vacíos con = NULL.
Usa IS NULL o IS NOT NULL.
Lanzar SELECT * sin LIMIT sobre una tabla de millones de filas.
Explora con LIMIT y pide solo las columnas que necesitas.
Asumir que 'lima' encontrará «Lima».
Según el sistema, = distingue mayúsculas. Limpia los datos o usa LOWER(region) = 'lima'.
3. ¿Qué devuelve SELECT COUNT(*) AS pedidos_grandes FROM ventas WHERE importe >= 600;?
COUNT(*) resume todas las filas que pasan el filtro en un solo número. Hay 15 pedidos de 600 USD o más: los mismos que FILTRAR devolvía en Excel en la lección 4.
4. ¿Cuál es el orden correcto de las cláusulas al escribir la consulta?
Se escribe SELECT, FROM, WHERE, ORDER BY y LIMIT. La base de datos la procesa en otro orden (FROM, WHERE, SELECT, ORDER BY, LIMIT), pero si cambias el orden de escritura obtendrás un error de sintaxis.
Ejercicio práctico
Escribe y ejecuta dos consultas en tu base nexo.db:
- Los pedidos de Bogotá por Marketplace con importe mayor a 100, del más caro al más barato (muestra id_pedido, fecha, producto e importe).
- Los pedidos de la categoría Oficina de 300 USD o más, con su región y canal, ordenados de mayor a menor importe.
Ver solución
-- 1
SELECT id_pedido, fecha, producto, importe
FROM ventas
WHERE region = 'Bogotá' AND canal = 'Marketplace' AND importe > 100
ORDER BY importe DESC;
| id_pedido | fecha | producto | importe |
|---|---|---|---|
| P-0154 | 2025-11-13 | Teclado mecánico | 164.7 |
| P-0173 | 2025-12-05 | Monitor 24 pulgadas | 159.9 |
| P-0009 | 2025-01-21 | Audífonos Bluetooth | 134.7 |
-- 2
SELECT id_pedido, fecha, region, canal, importe
FROM ventas
WHERE categoria = 'Oficina' AND importe >= 300
ORDER BY importe DESC;
| id_pedido | fecha | region | canal | importe |
|---|---|---|---|---|
| P-0030 | 2025-03-07 | La Libertad | Web | 756.0 |
| P-0166 | 2025-12-01 | Jalisco | Web | 567.0 |
| P-0013 | 2025-01-29 | Lima | Tienda física | 378.0 |
| P-0075 | 2025-06-16 | Ciudad de México | Marketplace | 378.0 |
En la segunda, todos son pedidos de sillas ergonómicas (189 USD cada una): 4, 3, 2 y 2 unidades. Si el empate entre P-0013 y P-0075 te sale en otro orden, es normal: agrega , id_pedido al ORDER BY para fijarlo.
Resumen
- SQL consulta bases de datos relacionales: tablas con tipos fijos que se relacionan por columnas comunes.
- SELECT elige columnas (con alias y cálculos), FROM la tabla y DISTINCT quita repetidos.
- WHERE filtra con comparaciones, BETWEEN, IN, LIKE e IS NULL; los textos van entre comillas simples.
- ORDER BY ordena y LIMIT corta: juntos responden preguntas de tipo «top N».
- Al mezclar AND y OR, usa paréntesis: 101 filas frente a 14 pueden ser la misma consulta mal escrita.
En la próxima lección: SQL intermedio: GROUP BY, HAVING y JOIN. Resumirás ventas por región y canal, filtrarás grupos y unirás la tabla de ventas con la de regiones para analizar por país.
Crea tu cuenta en 30 segundos, marca tus lecciones y descarga el certificado al terminar.