1. Faro
  2. Faro

Nivel 1 · Retención de clientes en SQL

La solicitud

De Catherine Hale, CEO

“La junta directiva no deja de preguntar si nuestros clientes vuelven. Quisiera la tasa de recompra por el año en que los clientes compraron por primera vez, un número a 90 días que sea justo con los clientes más nuevos y cómo se comparan los consumidores con las cuentas de empresa.”

Tu objetivo

Paso 1 de 3. Devuelve cohort_year (año del primer pedido completado del cliente), customers (clientes de la cohorte), repeat_customers (los que tienen 2 o más pedidos completados en total) y repeat_rate_pct (repeat_customers * 100 / customers, redondeado a 2). Ordena por cohort_year.

  • Lee de la tabla customerspendiente
  • Lee de la tabla orderspendiente
  • Muestra exactamente cohort_year customers repeat_customers repeat_rate_pctpendiente

Paso 1 · tu consulta

SQL

Cargando el editor…

    ResultadoVista previa de customers · presiona Ejecutar para ver el tuyo
    Vista previa de customers
    customer_idfirst_namelast_nameemailcity
    1OwenHudsonowen.hudson@icloud.comSan Francisco
    2DouglasMendozadmendoza69@gmail.comAtlanta
    3HenryBrookshbrooks@lakesidecafe.comLos Angeles
    4BrianGrahambgraham@bluecedarhomes.comSeattle

    Mote revisa tu resultado

    Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.

    Paso 2 · tu consulta

    SQL

    Cargando el editor…

      ResultadoVista previa de orders · presiona Ejecutar para ver el tuyo
      Vista previa de orders
      order_idcustomer_idorder_datestatuschannel
      10011252023-01-01 20:51:19completedweb
      10021562023-01-02 07:58:46completedweb
      1003222023-01-02 10:25:27completedmarketplace
      1004482023-01-02 11:21:51completedmarketplace

      Mote revisa tu resultado

      Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.

      Paso 3 · tu consulta

      SQL

      Cargando el editor…

        ResultadoVista previa de customers · presiona Ejecutar para ver el tuyo
        Vista previa de customers
        customer_idfirst_namelast_nameemailcity
        1OwenHudsonowen.hudson@icloud.comSan Francisco
        2DouglasMendozadmendoza69@gmail.comAtlanta
        3HenryBrookshbrooks@lakesidecafe.comLos Angeles
        4BrianGrahambgraham@bluecedarhomes.comSeattle

        Mote revisa tu resultado

        Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.

        Tablas

        Datos de la tienda Harbor & Pine, listos para consultar.

        customers2,018 filas · 9 columnas
        • customer_idinteger
        • first_namevarchar
        • last_namevarchar
        • emailvarchar
        • cityvarchar
        • statevarchar
        • signup_datedate
        • segmentvarchar
        • referral_sourcevarchar
        Primeras 5 filas de customers
        customer_idfirst_namelast_nameemailcitystatesignup_datesegmentreferral_source
        1OwenHudsonowen.hudson@icloud.comSan FranciscoCA2022-06-02consumersearch
        2DouglasMendozadmendoza69@gmail.comAtlantaGA2022-06-02consumerNULL
        3HenryBrookshbrooks@lakesidecafe.comLos AngelesCA2022-06-04businessNULL
        4BrianGrahambgraham@bluecedarhomes.comSeattleWA2022-06-06businesspinterest
        5KyleJimenezkjimenez27@yahoo.comIndianapolisIN2022-06-07consumerNULL
        orders12,000 filas · 7 columnas
        • order_idinteger
        • customer_idinteger
        • order_datetimestamp
        • statusvarchar
        • channelvarchar
        • shipping_feedecimal
        • coupon_codevarchar
        Primeras 5 filas de orders
        order_idcustomer_idorder_datestatuschannelshipping_feecoupon_code
        10011252023-01-01 20:51:19completedweb0.00NULL
        10021562023-01-02 07:58:46completedweb0.00NULL
        1003222023-01-02 10:25:27completedmarketplace4.99WELCOME10
        1004482023-01-02 11:21:51completedmarketplace4.99WELCOME10
        1005822023-01-02 11:43:36completedweb0.00WELCOME10
        products120 filas · 8 columnas
        • product_idinteger
        • namevarchar
        • categoryvarchar
        • subcategoryvarchar
        • unit_pricedecimal
        • unit_costdecimal
        • launched_ondate
        • is_activeboolean
        Primeras 5 filas de products
        product_idnamecategorysubcategoryunit_priceunit_costlaunched_onis_active
        101Vale Velvet LoveseatFurnitureSofas1659.00NULL2023-09-11true
        102Alder Oak Coffee TableFurnitureTables919.00503.502024-05-16true
        103Orchard Rattan Dining ChairFurnitureChairs789.00429.872023-05-27true
        104Upland Oak BookcaseFurnitureStorage429.00214.702022-12-20true
        105Avery Leather Bed FrameFurnitureBeds449.00232.702021-08-31true
        order_items30,338 filas · 6 columnas
        • order_item_idinteger
        • order_idinteger
        • product_idinteger
        • quantityinteger
        • unit_pricedecimal
        • discount_pctdecimal
        Primeras 5 filas de order_items
        order_item_idorder_idproduct_idquantityunit_pricediscount_pct
        11001121931.990.00
        21001154311.990.00
        31002154411.990.00
        410021941149.000.00
        51002193219.990.00
        returns1,673 filas · 5 columnas
        • return_idinteger
        • order_item_idinteger
        • returned_ondate
        • reasonvarchar
        • refund_amountdecimal
        Primeras 5 filas de returns
        return_idorder_item_idreturned_onreasonrefund_amount
        1142023-01-10damaged83.69
        2422023-01-10wrong_item19.99
        31052023-01-14wrong_item10.99
        4242023-01-16changed_mind26.99
        51262023-01-16late20.99
        employees60 filas · 8 columnas
        • employee_idinteger
        • first_namevarchar
        • last_namevarchar
        • departmentvarchar
        • titlevarchar
        • manager_idinteger
        • hire_datedate
        • salaryinteger
        Primeras 5 filas de employees
        employee_idfirst_namelast_namedepartmenttitlemanager_idhire_datesalary
        1CatherineHaleExecutiveChief Executive OfficerNULL2016-03-01285000
        2RobertKimExecutiveChief Operating Officer12017-01-09235000
        3MeganCollinsSalesHead of Sales12018-04-16172000
        4ChloeGutierrezSalesAccount Executive32020-01-2172000
        5FrankBellSalesAccount Executive32023-05-2173000
        web_sessions40,000 filas · 7 columnas
        • session_idinteger
        • customer_idinteger
        • started_attimestamp
        • devicevarchar
        • sourcevarchar
        • pages_viewedinteger
        • convertedboolean
        Primeras 5 filas de web_sessions
        session_idcustomer_idstarted_atdevicesourcepages_viewedconverted
        1NULL2023-01-01 23:02:24mobilepaid1false
        2182023-01-01 15:07:49desktoppaid1false
        3NULL2023-01-01 16:55:46desktopdirect7false
        4NULL2023-01-01 12:43:28mobileorganic3false
        5NULL2023-01-01 13:06:21mobilesocial2false
        marketing_spend3,276 filas · 3 columnas
        • spend_datedate
        • channelvarchar
        • amountdecimal
        Primeras 5 filas de marketing_spend
        spend_datechannelamount
        2023-01-01paid492.99
        2023-01-01email37.38
        2023-01-01social200.53
        2023-01-02paid709.13
        2023-01-02email64.13

        La lección

        Cómo lee un analista este ticket

        «¿Los clientes vuelven?» suena como un solo número. Se vuelve un número útil solo después de tres decisiones:

        1. Qué cuenta como pedido. Aquí, solo completed. Un pedido cancelado no es una compra, y un cliente cuyo único pedido fue reembolsado en realidad no empezó una relación con la tienda.
        2. Quién está en el denominador. Los clientes que compraron al menos una vez. Los ~230 registrados que nunca pidieron son un problema de adquisición, no de retención. Los 18 registros de clientes duplicados no tienen pedidos, así que quedan fuera por sí solos.
        3. Cuánto tiempo ha tenido cada cliente para volver. Esta es la decisión que la mayoría de los primeros intentos pasa por alto.

        Paso 1: cohortes por primer pedido

        Una cohorte es un grupo de clientes que empezaron al mismo tiempo. El primer paso es siempre el mismo: reducir orders a una fila por cliente con su primer pedido y su cantidad de pedidos. Todo lo que sigue es un GROUP BY sobre esta tabla pequeña. COUNT(*) FILTER (WHERE n_orders >= 2) cuenta los clientes recurrentes dentro de cada cohorte.

        Mira el resultado antes de seguir. La cohorte de 2025 tiene una tasa de recompra mucho más baja, y es tentador informar que la retención se está desplomando. No necesariamente: alguien que compró por primera vez en noviembre de 2025 ha tenido unas semanas para volver a pedir, mientras que un cliente de 2023 ha tenido tres años. Eso es censura por la derecha, y hace que cualquier tasa de recompra de toda la historia sea injusta con las cohortes recientes.

        Paso 2: una ventana justa de 90 días

        La solución estándar es una ventana fija: «recompra dentro de los 90 días siguientes al primer pedido», contada solo para los clientes que llevan al menos 90 días. Los datos terminan el 2025-12-31, así que los clientes cuyo primer pedido es anterior al 2025-10-01 son elegibles.

        Para encontrar el segundo pedido de cada cliente, numera sus pedidos completados con ROW_NUMBER(), ordenados por order_date y luego por order_id para que los empates se resuelvan siempre igual. Un MAX condicional pivota rn = 1 y rn = 2 en dos columnas. Un comprador de una sola vez tiene NULL en second_order, y NULL <= cualquier cosa no es verdadero, así que correctamente no pasa la prueba sin ningún tratamiento especial.

        Compara tiempos con first_order + INTERVAL 90 DAY en lugar de date_diff('day', ...). date_diff cuenta límites de días calendario, mientras que la aritmética de intervalos mide exactamente 90 × 24 horas. Ambas son defendibles; usar el intervalo hace que las versiones de SQL y pandas coincidan hasta el último cliente.

        Paso 3: consumidores frente a empresas

        Une la tabla por cliente con customers para obtener el segmento. El JOIN es uno a uno, porque ambos lados tienen una fila por cliente, así que nada se cuenta doble. Las cuentas de empresa son pocas, pero piden con mucha más frecuencia, y por eso la junta debería verlas por separado: una tasa de recompra combinada describe sobre todo a los consumidores.

        SQL y pandas lado a lado

        Idea SQL pandas
        una fila por cliente GROUP BY customer_id en una CTE groupby("customer_id").agg(...)
        n-ésimo pedido ROW_NUMBER() OVER (PARTITION BY ...) sort_values + groupby().cumcount()
        más 90 días + INTERVAL 90 DAY + pd.Timedelta(days=90)
        año de una fecha year(first_order) .dt.year

        Errores comunes

        • Contar pedidos no completados. Un primer pedido cancelado pondría a un cliente en la cohorte equivocada.
        • Unir primero customers con orders y luego contar. COUNT(*) sobre ese JOIN cuenta pedidos, no clientes; agrega primero a una fila por cliente.
        • Comparar cohortes con distinta exposición. Informa la tasa de 90 días junto a la tasa de toda la historia, y explica por qué.

        Elige qué puede ejecutarse en este sitio. Puedes cambiarlo cuando quieras desde "Configurar cookies", al pie de cada página.