Aprende SQL fácil y rápido desde cero
1. Para qué sirve SQL
SQL (Structured Query Language) sirve para consultar datos de una tabla o de un conjunto de tablas.
2. Dónde se usa SQL
SQL no vive en un solo lugar — es un lenguaje que aparece en múltiples herramientas.
2.1 Bases de datos transaccionales (OLTP)
PostgreSQL, MySQL, SQL Server. Son las bases de datos de las aplicaciones: aquí se registra un usuario nuevo, se guarda un pedido, se actualiza un pago. Muchas operaciones pequeñas en tiempo real. Aquí vive el mundo del desarrollo de software.
2.2 Bases de datos analíticas (OLAP)
BigQuery (Google), Snowflake, Redshift (AWS). Son las bases de datos del análisis de datos. Aquí no hay operaciones individuales — hay millones de filas históricas que necesitas consultar para entender tendencias, comportamientos, métricas. Aquí vive el mundo del analista de datos.
2.3 Herramientas de BI
Power BI, Looker, Metabase. Aunque no lo parezca visualmente, por debajo están corriendo SQL. En Power BI incluso puedes escribirlo directamente si sabes cómo.
2.4 Python y código
Si trabajas con datos en Python, tarde o temprano vas a conectarte a una base de datos y vas a escribir SQL desde tu script para traer los datos que necesitas.
2.5 Google Sheets
La función QUERY() de Sheets usa una sintaxis prácticamente idéntica a SQL. Si ya la usas, tienes más base de la que crees.
2.6 OLTP vs OLAP — La diferencia que más importa
| OLTP | OLAP | |
|---|---|---|
| Ejemplos | PostgreSQL, MySQL | BigQuery, Snowflake |
| Operación dominante | Escritura y lectura pequeña | Lectura masiva |
| Volumen típico | Miles de filas | Millones / miles de millones |
| Quién lo usa | Desarrolladores backend | Analistas de datos |
2.7 Los dialectos: SQL no es igual en todos lados
SQL tiene una base común, pero cada herramienta tiene su propia variante. BigQuery usa GoogleSQL, PostgreSQL tiene sus propias funciones, MySQL difiere en manejo de fechas y texto. La lógica es la misma, pero hay funciones que funcionan en uno y no en otro.
3. Dónde ejecutar tus primeras queries
Hay muchos lugares donde puedes escribir y correr SQL:
- Herramientas online sin instalación:
sqliteonline.com,db-fiddle.com - BigQuery console (Google Cloud)
- DBeaver (cliente universal de escritorio)
- pgAdmin (específico para PostgreSQL)
- MySQL Workbench (específico para MySQL)
- Power BI, Metabase, Looker (dentro de herramientas de BI)
- Jupyter notebooks con Python
3.1 BigQuery
Es la herramienta de Google Cloud para consultar datos a escala. Tiene una consola web donde puedes escribir SQL directamente sin instalar nada. El free tier incluye 1TB de consultas por mes, lo que es más que suficiente para aprender.
Lo más valioso para practicar: BigQuery tiene datasets públicos — datos reales de taxis de NY, Wikipedia, GitHub, COVID, entre otros — que puedes consultar desde el primer día sin tener que cargar nada.
3.2 DBeaver
Es un cliente de escritorio gratuito que se conecta a prácticamente cualquier base de datos: PostgreSQL, MySQL, SQL Server, BigQuery, SQLite, y más. Es la navaja suiza del analista de datos.
Lo usas cuando trabajas con bases de datos que no tienen su propia interfaz web o cuando quieres un entorno más completo que una consola en el browser.
4. El dataset de práctica lo veremos en Big Query
Big query no es una base de datos relaciona, es un Datawhare house que permite almacenar tablas con miles de filas y hacer consultas SQL. Está enfocado principalmente para almacenar datos de análisis (OLAP), es decir, datos que no se usan en tiempo real en una aplicación (en algunos casos hay excepciones sobre este punto) y que sirven mayormente para análisis.
Para todos los ejemplos de este post voy a usar un dataset público que creé con datos reales de mi canal de YouTube. Puedes consultarlo directamente desde BigQuery sin necesidad de cargar nada.
Dataset: youtube-datasets-360.angelgarciadatablog
Para acceder necesitas:
- Tener una cuenta de Google
- Crear un proyecto en Google Cloud Console — puede ser cualquier nombre, es solo para tener acceso a BigQuery
- Ir a BigQuery y buscar el dataset
youtube-datasets-360.angelgarciadatablogen el explorador
5. Cómo entender una tabla antes de escribir una query
Antes de escribir cualquier query hay que conocer la tabla. El primer paso es consultar el schema:
SELECT field_path, data_type, description
FROM `youtube-datasets-360.region-us`.INFORMATION_SCHEMA.COLUMN_FIELD_PATHS
WHERE table_schema = 'angelgarciadatablog'
AND table_name = 'videos_static'
Esto devuelve el nombre de cada columna, su tipo de dato y su descripción. Si description sale NULL, el schema no está documentado — más común de lo que parece en entornos reales.
Gotcha de región: si te sale
Dataset was not found in location US, usa la sintaxis conregion-usen lugar del nombre del dataset y agregaWHERE table_schema = 'nombre_dataset'.Gotcha de permisos:
COLUMN_FIELD_PATHSsolo funciona cuando eres dueño del dataset. Desde un proyecto externo usaINFORMATION_SCHEMA.COLUMNS— siempre accesible con permisos de lectura, pero sin el campodescription.
Con el schema en mano, recorre este checklist:
4.1 Checklist de exploración inicial
1. Granularidad — ¿Qué representa una fila?
Es la pregunta más importante. En videos_static una fila = un video. En videos_snapshot una fila = un video en una fecha específica. Confundir la granularidad genera errores difíciles de detectar porque los números salen, pero están mal.
2. Entidad principal — ¿Qué ID identifica de forma única cada fila?
El ID que hace que esa fila sea irrepetible. En ventas sería order_id, aquí video_id.
3. IDs secundarios — ¿Qué otras entidades están presentes?
Son tus relaciones implícitas. channel_id en videos_static te dice que puedes cruzarla con channels_static.
4. Dimensión temporal — ¿Cómo vive el tiempo en esta tabla?
Hay dos patrones principales:
- Write Truncate: la tabla siempre refleja el estado actual. Cada carga borra y reescribe todo. No hay historia — solo el presente.
- Write Append: la tabla acumula registros en el tiempo. Tiene una columna de fecha que marca cada carga. Si no filtras por esa fecha, estás agregando todos los snapshots históricos como si fueran uno solo.
También existen Upsert/Merge (actualiza si existe, inserta si no) y Partition Overwrite (reescribe solo la partición del día), pero los dos anteriores cubren la mayoría de los casos.
5. Frecuencia de actualización — ¿Cada cuánto se refresca el dato? No es lo mismo analizar una tabla que se actualiza en tiempo real que una que carga una vez al día. Cambia cómo interpretas los resultados.
6. Métricas disponibles — ¿Qué números puedes agregar?
view_count, duration_seconds, subscriber_count — los números que vas a sumar, promediar o comparar.
7. Calidad del dato — ¿Hay NULLs? ¿Hay duplicados en el ID principal?
8. Cardinalidad de las dimensiones — ¿Cuántos valores únicos tiene cada columna?
Las dimensiones con pocos valores distintos son las que vas a usar en filtros, agrupaciones y gráficos. El flujo para identificarlas:
Paso 1 — contar valores únicos por columna:
SELECT
COUNT(DISTINCT channel_id) AS channel_id,
COUNT(DISTINCT category_id) AS category_id,
COUNT(DISTINCT title) AS title,
COUNT(DISTINCT duration_seconds) AS duration_seconds,
COUNT(DISTINCT video_id) AS video_id
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
Paso 2 — para las columnas con cardinalidad baja (menos de ~20 valores), ver sus valores reales:
SELECT DISTINCT category_id, channel_id
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
ORDER BY category_id
Este flujo manual es lo que hace cualquier analista cuando llega a un dataset sin documentación. Lo que lo hace más eficiente no son las queries sino tener el schema documentado desde el principio.
6. SQL con una sola tabla
Para responder consultas básicas de negocio con una sola tabla, solo necesitas las cláusulas principales de SQL. Si vienes de Excel, ya tienes el modelo mental — solo cambia la sintaxis.
6.1 Preguntas de negocio
Antes de escribir una sola query, definimos qué queremos saber. Sobre videos_static:
- ¿Cuántos videos tengo publicados?
- ¿Cuál es la duración promedio de mis videos?
- ¿Cuántos videos tengo por categoría?
- ¿Cuáles son mis videos más largos?
- ¿Qué videos duran más de 10 minutos?
- ¿En qué meses publiqué más de 5 videos?
6.2 Las cláusulas esenciales
6.2.1 SELECT
Elige qué columnas quieres ver. Como mostrar u ocultar columnas en Excel.
SELECT title, duration_seconds
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
* devuelve todas las columnas. Úsalo solo para explorar — en producción siempre especifica las columnas que necesitas.
SELECT *
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
6.2.2 WHERE
Filtra filas. Como el filtro de Excel pero escrito como condición.
SELECT title, duration_seconds
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
WHERE duration_seconds > 600
Operadores disponibles: =, !=, >, <, >=, <=, IN, LIKE, IS NULL, BETWEEN.
6.2.3 GROUP BY y funciones de agregación
Agrupa filas y calcula algo sobre cada grupo. Como crear una tabla dinámica en Excel.
Sin GROUP BY no puedes usar funciones de agregación sobre subconjuntos del dato:
| Función | Qué hace |
|---|---|
COUNT(*) |
Cuenta filas |
SUM(columna) |
Suma valores |
AVG(columna) |
Promedia valores |
MIN(columna) |
Valor mínimo |
MAX(columna) |
Valor máximo |
SELECT category_id, COUNT(*) as total
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
GROUP BY category_id
6.2.4 HAVING
Filtra los resultados de un GROUP BY. Como filtrar en una tabla dinámica de Excel.
La diferencia con WHERE: WHERE filtra filas antes de agrupar, HAVING filtra grupos después de agrupar. Por eso no puedes usar WHERE con una función de agregación — WHERE se ejecuta antes de que existan los grupos.
SELECT category_id, COUNT(*) as total
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
GROUP BY category_id
HAVING total > 5
6.2.5 ORDER BY
Ordena los resultados. Como ordenar una columna en Excel.
SELECT title, duration_seconds
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
ORDER BY duration_seconds DESC
DESC ordena de mayor a menor. ASC de menor a mayor (es el default).
6.2.6 CASE WHEN
No es una cláusula principal, pero es tan útil que merece estar aquí. Permite crear categorías personalizadas a partir de los valores de una columna.
El problema concreto: category_id en videos_static solo tiene los valores 22 y 27 — los IDs de categoría de YouTube, que no dicen nada útil. Con CASE podemos crear nuestras propias categorías evaluando el título del video:
SELECT
CASE
WHEN LOWER(title) LIKE '%sql%' THEN 'sql'
WHEN LOWER(title) LIKE '%power bi%' THEN 'power-bi'
WHEN LOWER(title) LIKE '%python%' THEN 'python'
WHEN LOWER(title) LIKE '%git%' THEN 'git'
WHEN LOWER(title) LIKE '%bigquery%' OR LOWER(title) LIKE '%big query%' OR LOWER(title) LIKE '%google cloud%' THEN 'google-cloud'
WHEN LOWER(title) LIKE '%bash%' OR LOWER(title) LIKE '%shell%' THEN 'bash-shell'
WHEN LOWER(title) LIKE '%api%' THEN 'apis'
WHEN LOWER(title) LIKE '%excel%' OR LOWER(title) LIKE '%sheets%' THEN 'hojas-de-calculo'
WHEN LOWER(title) LIKE '%google analytics%' OR LOWER(title) LIKE '%ga4%' THEN 'google-analytics'
WHEN LOWER(title) LIKE '%html%' OR LOWER(title) LIKE '%css%' OR LOWER(title) LIKE '%web%' THEN 'portafolio-web'
WHEN LOWER(title) LIKE '%claude%' THEN 'claude-code'
ELSE 'sin-categoria'
END AS categoria,
COUNT(*) as total
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
GROUP BY categoria
ORDER BY total DESC
Resultado:
| categoria | total |
|---|---|
| sql | 88 |
| power-bi | 51 |
| sin-categoria | 19 |
| google-analytics | 12 |
| google-cloud | 8 |
| hojas-de-calculo | 8 |
| python | 5 |
| git | 4 |
| apis | 1 |
La lógica de CASE es secuencial — evalúa cada condición en orden y asigna el primer valor que se cumple. Si ninguna condición aplica, cae en ELSE.
Lo pusimos en SELECT porque queremos crear una nueva columna en el resultado — eso es exactamente lo que hace SELECT: define qué columnas vas a ver y cómo se calculan.
Pero CASE puede ir en otros lugares también:
- GROUP BY — para agrupar por una expresión condicional
- ORDER BY — para definir un orden personalizado
- HAVING — para filtrar grupos con lógica condicional
En BigQuery puedes referenciar el alias directamente en GROUP BY y ORDER BY (como en el ejemplo anterior con GROUP BY categoria), así evitas repetir todo el CASE.
6.2.7 Orden de ejecución
SQL no se ejecuta en el orden en que se escribe. El orden real es:
1. FROM → de qué tabla traer los datos
2. WHERE → filtrar filas
3. GROUP BY → agrupar
4. HAVING → filtrar grupos
5. SELECT → elegir columnas (aquí se evalúa el CASE)
6. ORDER BY → ordenar
Esto explica por qué no puedes usar un alias definido en SELECT dentro de un WHERE — SELECT se ejecuta después de WHERE, así que el alias todavía no existe. HAVING viene después de GROUP BY, por eso puede filtrar valores agregados como COUNT() y WHERE no puede.
6.3 Respondiendo las preguntas
1. ¿Cuántos videos tengo publicados?
SELECT COUNT(*) as total_videos
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
→ 196 videos
2. ¿Cuál es la duración promedio de mis videos?
SELECT ROUND(AVG(duration_seconds / 60), 1) as duracion_promedio_minutos
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
→ 12.8 minutos
3. ¿Cuántos videos tengo por categoría?
SELECT category_id, COUNT(*) as total
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
GROUP BY category_id
ORDER BY total DESC
→ categoría 27: 194 videos / categoría 22: 2 videos
4. ¿Cuáles son mis videos más largos?
SELECT title, ROUND(duration_seconds / 60, 1) as minutos
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
ORDER BY duration_seconds DESC
5. ¿Qué videos duran más de 10 minutos?
SELECT title, ROUND(duration_seconds / 60, 1) as minutos
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
WHERE duration_seconds > 600
ORDER BY duration_seconds DESC
6. ¿En qué meses publiqué más de 5 videos?
SELECT FORMAT_TIMESTAMP('%Y-%m', published_at) as mes, COUNT(*) as total
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
GROUP BY mes
HAVING total > 5
ORDER BY mes DESC
→ Los meses más activos fueron septiembre y octubre 2025 con 37 y 35 videos.
7. Más allá de lo básico: CTEs y funciones de ventana
7.1 CTEs — tablas temporales de consulta
Un CTE (Common Table Expression) es un resultado intermedio que defines antes de la query principal. Existe solo durante la ejecución — no se guarda en ningún lado. Se declara con WITH.
Lo más útil de los CTEs es que puedes encadenarlos: cada uno puede usar al anterior como fuente. Eso permite construir queries complejas en pasos legibles:
WITH por_anio AS (
SELECT EXTRACT(YEAR FROM published_at) AS anio, COUNT(*) AS total
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
GROUP BY anio
),
anios_activos AS (
SELECT anio, total
FROM por_anio
WHERE total > 10
),
resumen AS (
SELECT COUNT(*) AS anios_activos, SUM(total) AS total_videos, ROUND(AVG(total), 1) AS promedio_por_anio
FROM anios_activos
)
SELECT * FROM resumen
por_anio— agrupa videos por año con GROUP BYanios_activos— filtra solo los años con más de 10 videosresumen— saca el total y promedio de esos años
Sin CTEs, esta lógica viviría en subqueries anidadas que son difíciles de leer. Con CTEs cada paso tiene nombre y se puede razonar por separado.
7.2 Funciones de ventana
Una función de ventana hace un cálculo sobre un conjunto de filas relacionadas sin colapsar el resultado. Esa es la diferencia clave con GROUP BY: GROUP BY te da una fila por grupo, una función de ventana agrega el cálculo como columna nueva manteniendo todas las filas.
La más básica y usada es ROW_NUMBER() — numera cada fila dentro de una partición según un orden definido:
SELECT
title,
EXTRACT(YEAR FROM published_at) AS anio,
ROUND(duration_seconds/60,1) AS minutos,
ROW_NUMBER() OVER (PARTITION BY EXTRACT(YEAR FROM published_at) ORDER BY duration_seconds DESC) AS ranking_del_anio
FROM `youtube-datasets-360.angelgarciadatablog.videos_static`
ORDER BY anio DESC, ranking_del_anio
PARTITION BY— define los grupos (aquí: cada año es una partición)ORDER BY— define el criterio de numeración dentro del grupo (aquí: duración descendente)
El resultado: dentro de cada año, tus videos numerados del más largo al más corto. El que tiene ranking_del_anio = 1 es el video más largo de ese año.
8. Mi conclusión
Si vienes de Excel, SQL no es tan diferente como parece. Un filtro es un WHERE, una tabla dinámica es un GROUP BY, ordenar una columna es un ORDER BY. La lógica es la misma — solo cambia la sintaxis.
Lo que cambia de verdad es la escala y el poder. Con una sola tabla y las herramientas de este post puedes filtrar millones de filas en segundos, crear categorías personalizadas con CASE, encadenar transformaciones con CTEs y calcular rankings dentro de grupos con funciones de ventana — cosas que en Excel requerirían fórmulas complejas o simplemente no serían posibles.
Y esto es solo con una tabla. Cuando empezamos a cruzar tablas con JOINs, el salto es aún mayor.
9. Pendientes
- [ ] Grabar video: cómo empezar a usar BigQuery desde cero para principiantes — creación de proyecto en Google Cloud Console, navegación de la consola, primer query. Va referenciado en la sección 4.
- [ ] Post + video especial sobre CASE WHEN: cubrir todos los contextos donde se puede usar — SELECT, GROUP BY, ORDER BY, HAVING — con ejemplos prácticos para cada uno.