1. Faro

Nivel 8 · Desafío final: el paquete de KPI heredado

La solicitud

De Priya Shah, Directora de Datos

“La analista que construyó nuestro paquete mensual de KPI se ha ido, y Catherine lo quiere para la junta el viernes. Las tres consultas se ejecutan, y las tres me parecen raras. Por favor arréglalas y dime qué estaba mal.”

Tu objetivo

Paso 1 de 3. Para los pedidos completados realizados en 2025, devuelve una fila por mes con: month (un DATE, el primero del mes), orders, net_revenue (redondeado a 2) y shipping_revenue (el shipping_fee de cada pedido contado una vez, redondeado a 2). Ordena por month ascendente.

  • Lee de la tabla orderspendiente
  • Lee de la tabla order_itemspendiente
  • Muestra exactamente month orders net_revenue shipping_revenuependiente

Paso 1 · 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 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.

        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
        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
        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
        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
        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

        Los informes heredados rara vez tienen un solo fallo. Este desafío final te da tres consultas del mismo paquete. Cada una se ejecuta, cada una devuelve una tabla ordenada, y cada una le dice algo falso a la junta.

        Paso 1: ingresos mensuales y envío

        Dos fallos de este capítulo. El join con order_items repite el shipping_fee de cada pedido una vez por línea, así que el envío está inflado en todos los meses: enero muestra $1,223.15 en lugar de $752.17. Y BETWEEN '2025-01-01' AND '2025-12-31' se detiene a medianoche del 31 de diciembre, así que diciembre tiene 623 pedidos en lugar de 634.

        Arregla primero la multiplicación de filas sumando las líneas por pedido en una CTE. Luego cambia a un rango semiabierto. El conteo de pedidos, los ingresos y el total de envío deberían coincidir después con el sistema de pedidos.

        Paso 2: tasa de cancelación por canal

        El starter devuelve 12 filas para 3 canales. GROUP BY channel, status divide cada canal en una fila por estado, así que la fila de cancelados muestra 100% y todas las demás 0%. Es un fallo de grano: la columna extra hace el agrupamiento más fino que la pregunta.

        Arregla el agrupamiento y aparece un segundo fallo: // es división entera, así que la tasa de app se lee 6 en lugar de 6.23. Usa 100.0 * y divide con /.

        Paso 3: tasa de recompra por segmento

        Una tasa de recompra de exactamente 100% en ambos segmentos es imposible. El HAVING COUNT(*) >= 2 dentro de la CTE elimina a los compradores de una sola vez antes de que la consulta externa cuente compradores, así que numerador y denominador son el mismo grupo. Es la lección de WHERE frente a HAVING en otra forma: un filtro aplicado en el paso equivocado cambia lo que significa el agregado.

        Quita el HAVING. El FILTER (WHERE b.orders >= 2) externo ya restringe el numerador. Empresas tiene 253 compradores y 188 recurrentes, consumidores 1,194 y 690.

        Cómo trabajar un paquete roto

        1. Para cada consulta, di en una frase qué debería ser una fila de salida. Compáralo con el conteo de filas.
        2. Reconstruye un número desde la consulta más simple posible: el envío solo desde orders, el conteo de diciembre con un rango semiabierto, los compradores con un simple COUNT(DISTINCT customer_id).
        3. Arregla un fallo, vuelve a ejecutar, compara otra vez. Un segundo fallo suele esconderse detrás del primero.

        Errores comunes

        • Tratar el COUNT(DISTINCT o.order_id) del paso 1 como prueba de que la consulta no tiene multiplicación de filas. Protege el conteo, no las sumas.
        • Arreglar la división del paso 2 pero no su agrupamiento: las filas de 0% y 100% siguen mal.
        • Mover el HAVING del paso 3 a la consulta externa: eso filtra segmentos, no clientes.

        En SQL Server

        T-SQL no tiene cláusula FILTER. Escribe los conteos condicionales con CASE, que también funciona en DuckDB:

        SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) AS cancelled_orders,
        ROUND(100.0 * SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) / COUNT(*), 2)

        En el trabajo

        Anota qué estaba mal en cada consulta cuando devuelvas el paquete. La junta preguntará por qué cambiaron los números del trimestre pasado, y “el envío se contaba una vez por línea” es una respuesta que pueden aceptar.

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