La solicitud

De Megan Collins, Directora de Ventas
Tu objetivo
Paso 1 de 3. Devuelve customer_id y first_order_date (la parte de fecha, como DATE, de su pedido completado más temprano) de todo cliente con al menos un pedido completado.
- Lee de la tabla
orderspendiente - Muestra exactamente
customer_idfirst_order_datependiente - 1,730 filaspendiente
Paso 2 de 3. Devuelve una fila por mes de 2025 con: month (primer día del mes, como DATE) y new_customers (clientes cuyo primer pedido completado cae en ese mes). Ordena por month ascendente.
- Lee de la tabla
orderspendiente - Muestra exactamente
monthnew_customerspendiente - 12 filas, ordenadas como se pidependiente
Paso 3 de 3. Toma los clientes cuyo primer pedido completado se hizo en 2024. Devuelve una fila con: cohort_customers (cuántos), repeat_90d (cuántos hicieron otro pedido completado después del primero y no más tarde de 90 días después de él, es decir, order_date > first_order_at y order_date <= first_order_at + INTERVAL '90 days') y repeat_rate_pct (repeat_90d / cohort_customers * 100, redondeado a 1).
- Lee de la tabla
orderspendiente - Muestra exactamente
cohort_customersrepeat_90drepeat_rate_pctpendiente - 1 filapendiente
Paso 1 · tu consulta
Cargando el editor…
Solución de referencia
| order_id | customer_id | order_date | status | channel |
|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace |
Mote revisa tu resultado
Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.
Paso 2 · tu consulta
Cargando el editor…
Solución de referencia
| order_id | customer_id | order_date | status | channel |
|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace |
Mote revisa tu resultado
Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.
Paso 3 · tu consulta
Cargando el editor…
Solución de referencia
| order_id | customer_id | order_date | status | channel |
|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace |
Mote revisa tu resultado
Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.
Lo que recibe Megan, y por qué
Tablas
Datos de la tienda Harbor & Pine, listos para consultar.
orders12,000 filas · 7 columnas
order_idintegercustomer_idintegerorder_datetimestampstatusvarcharchannelvarcharshipping_feedecimalcoupon_codevarchar
| order_id | customer_id | order_date | status | channel | shipping_fee | coupon_code |
|---|---|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web | 0.00 | NULL |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web | 0.00 | NULL |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace | 4.99 | WELCOME10 |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace | 4.99 | WELCOME10 |
| 1005 | 82 | 2023-01-02 11:43:36 | completed | web | 0.00 | WELCOME10 |
customers2,018 filas · 9 columnas
customer_idintegerfirst_namevarcharlast_namevarcharemailvarcharcityvarcharstatevarcharsignup_datedatesegmentvarcharreferral_sourcevarchar
| customer_id | first_name | last_name | city | state | signup_date | segment | referral_source | |
|---|---|---|---|---|---|---|---|---|
| 1 | Owen | Hudson | owen.hudson@icloud.com | San Francisco | CA | 2022-06-02 | consumer | search |
| 2 | Douglas | Mendoza | dmendoza69@gmail.com | Atlanta | GA | 2022-06-02 | consumer | NULL |
| 3 | Henry | Brooks | hbrooks@lakesidecafe.com | Los Angeles | CA | 2022-06-04 | business | NULL |
| 4 | Brian | Graham | bgraham@bluecedarhomes.com | Seattle | WA | 2022-06-06 | business | |
| 5 | Kyle | Jimenez | kjimenez27@yahoo.com | Indianapolis | IN | 2022-06-07 | consumer | NULL |
products120 filas · 8 columnas
product_idintegernamevarcharcategoryvarcharsubcategoryvarcharunit_pricedecimalunit_costdecimallaunched_ondateis_activeboolean
| product_id | name | category | subcategory | unit_price | unit_cost | launched_on | is_active |
|---|---|---|---|---|---|---|---|
| 101 | Vale Velvet Loveseat | Furniture | Sofas | 1659.00 | NULL | 2023-09-11 | true |
| 102 | Alder Oak Coffee Table | Furniture | Tables | 919.00 | 503.50 | 2024-05-16 | true |
| 103 | Orchard Rattan Dining Chair | Furniture | Chairs | 789.00 | 429.87 | 2023-05-27 | true |
| 104 | Upland Oak Bookcase | Furniture | Storage | 429.00 | 214.70 | 2022-12-20 | true |
| 105 | Avery Leather Bed Frame | Furniture | Beds | 449.00 | 232.70 | 2021-08-31 | true |
order_items30,338 filas · 6 columnas
order_item_idintegerorder_idintegerproduct_idintegerquantityintegerunit_pricedecimaldiscount_pctdecimal
| order_item_id | order_id | product_id | quantity | unit_price | discount_pct |
|---|---|---|---|---|---|
| 1 | 1001 | 121 | 9 | 31.99 | 0.00 |
| 2 | 1001 | 154 | 3 | 11.99 | 0.00 |
| 3 | 1002 | 154 | 4 | 11.99 | 0.00 |
| 4 | 1002 | 194 | 1 | 149.00 | 0.00 |
| 5 | 1002 | 193 | 2 | 19.99 | 0.00 |
returns1,673 filas · 5 columnas
return_idintegerorder_item_idintegerreturned_ondatereasonvarcharrefund_amountdecimal
| return_id | order_item_id | returned_on | reason | refund_amount |
|---|---|---|---|---|
| 1 | 14 | 2023-01-10 | damaged | 83.69 |
| 2 | 42 | 2023-01-10 | wrong_item | 19.99 |
| 3 | 105 | 2023-01-14 | wrong_item | 10.99 |
| 4 | 24 | 2023-01-16 | changed_mind | 26.99 |
| 5 | 126 | 2023-01-16 | late | 20.99 |
employees60 filas · 8 columnas
employee_idintegerfirst_namevarcharlast_namevarchardepartmentvarchartitlevarcharmanager_idintegerhire_datedatesalaryinteger
| employee_id | first_name | last_name | department | title | manager_id | hire_date | salary |
|---|---|---|---|---|---|---|---|
| 1 | Catherine | Hale | Executive | Chief Executive Officer | NULL | 2016-03-01 | 285000 |
| 2 | Robert | Kim | Executive | Chief Operating Officer | 1 | 2017-01-09 | 235000 |
| 3 | Megan | Collins | Sales | Head of Sales | 1 | 2018-04-16 | 172000 |
| 4 | Chloe | Gutierrez | Sales | Account Executive | 3 | 2020-01-21 | 72000 |
| 5 | Frank | Bell | Sales | Account Executive | 3 | 2023-05-21 | 73000 |
web_sessions40,000 filas · 7 columnas
session_idintegercustomer_idintegerstarted_attimestampdevicevarcharsourcevarcharpages_viewedintegerconvertedboolean
| session_id | customer_id | started_at | device | source | pages_viewed | converted |
|---|---|---|---|---|---|---|
| 1 | NULL | 2023-01-01 23:02:24 | mobile | paid | 1 | false |
| 2 | 18 | 2023-01-01 15:07:49 | desktop | paid | 1 | false |
| 3 | NULL | 2023-01-01 16:55:46 | desktop | direct | 7 | false |
| 4 | NULL | 2023-01-01 12:43:28 | mobile | organic | 3 | false |
| 5 | NULL | 2023-01-01 13:06:21 | mobile | social | 2 | false |
marketing_spend3,276 filas · 3 columnas
spend_datedatechannelvarcharamountdecimal
| spend_date | channel | amount |
|---|---|---|
| 2023-01-01 | paid | 492.99 |
| 2023-01-01 | 37.38 | |
| 2023-01-01 | social | 200.53 |
| 2023-01-02 | paid | 709.13 |
| 2023-01-02 | 64.13 |
La lección
Las preguntas de retención empiezan todas igual: encuentra el primer evento de cada cliente y luego mira lo que pasa después. Los CTE están hechos para esto, porque cada paso tiene un grano claro.
Paso 1: primer pedido por cliente
SELECT customer_id, CAST(MIN(order_date) AS DATE) AS first_order_date
FROM orders
WHERE status = 'completed'
GROUP BY customer_id;
Una fila por cliente. Si un pedido cancelado cuenta como “captar” a un cliente es una definición de negocio. El ticket de Megan dice solo completados, así que el filtro va antes del MIN. Si lo pones después, un cliente cuyo primer pedido fue cancelado se captaría en la fecha equivocada.
Paso 2: clientes nuevos por mes
Con los primeros pedidos en un CTE, “clientes nuevos por mes” es un DATE_TRUNC y un conteo. El filtro de año
se aplica a first_order_at, no a todos los pedidos: un cliente que compró por primera vez en 2023 y de nuevo en 2025
no es nuevo en 2025. Confundir esos dos filtros es uno de los errores más comunes en los informes de captación.
Paso 3: tasa de recompra a 90 días
Define la cohorte (primer pedido completado en 2024) y luego marca a cada miembro que volvió:
CASE WHEN EXISTS (
SELECT 1 FROM orders AS o
WHERE o.customer_id = c.customer_id
AND o.status = 'completed'
AND o.order_date > c.first_order_at
AND o.order_date <= c.first_order_at + INTERVAL '90 days'
) THEN 1 ELSE 0 END AS repeated
EXISTS busca al menos un pedido que califique, así que un cliente con cinco pedidos de recompra se
cuenta una sola vez. Un JOIN aquí generaría fan-out e inflaría el numerador. El > estricto excluye el primer
pedido en sí. La tasa es la suma de las banderas sobre el tamaño de la cohorte.
Usar una ventana fija (90 días desde el primer pedido de cada cliente) es deliberado. “Volvió a pedir en 2024” daría a los clientes de diciembre casi nada de tiempo para volver y a los de enero un año entero, lo que hace que la tasa dependa de cuándo se unió la gente y no de lo leal que es.
INTERVAL '90 days' funciona en DuckDB y Postgres. MySQL y BigQuery escriben INTERVAL 90 DAY
(BigQuery dentro de TIMESTAMP_ADD).
Convertirlo en una historia
Una sola tasa de recompra es útil; una tendencia es mejor. Agrupa el paso 3 por el mes del primer pedido y tendrás una tabla de cohortes: ¿los clientes captados en primavera volvieron más que los de la temporada navideña? Ese es el Desafío final del próximo capítulo, construido con funciones de ventana.
Errores comunes
- Filtrar el estado después de tomar el MIN. Si los pedidos cancelados siguen dentro cuando se ejecuta
MIN(order_date), un cliente cuyo primer pedido fue cancelado obtiene la fecha de primera compra equivocada. PonWHERE status = 'completed'en la misma consulta que el MIN. - Filtrar el año sobre todos los pedidos.
WHERE order_date >= '2025-01-01'antes del MIN hace que un cliente de 2023 parezca nuevo en 2025. Filtra porfirst_order_atuna vez calculado. - Usar
>=en la ventana de recompra.o.order_date >= c.first_order_atcoincide con el propio primer pedido, así que todos los clientes cuentan como recompra. Usa>. - Unir con los pedidos de recompra en lugar de usar EXISTS. Un cliente con cinco pedidos de recompra se cuenta cinco veces en el numerador.
En SQL Server
Reemplaza c.first_order_at + INTERVAL '90 days' por DATEADD(day, 90, c.first_order_at). Los CTE y
MIN(order_date) no cambian, y la clave de mes es DATETRUNC(month, first_order_at) (SQL Server 2022)
o DATEFROMPARTS(YEAR(first_order_at), MONTH(first_order_at), 1).
¡Linterna encendida!
Practicaste: cteminexistsretentioncohort
- Resuelto
- Sin pistas
- Al primer intento
