1. Faro

Nivel 10 · Desafío final: cuadro de mando por categoría

La solicitud

De Megan Collins, Directora de Ventas

“Para la revisión trimestral del negocio necesito un cuadro de mando por categoría: ingresos completados por categoría, la tasa de devolución por categoría y nuestros cinco clientes más grandes por ingresos completados, con sus nombres.”

Tu objetivo

Paso 1 de 3. Devuelve category y net_revenue (redondeado a 2) de los pedidos completados, una fila por categoría.

  • Lee de la tabla productspendiente
  • Lee de la tabla orderspendiente
  • Muestra exactamente category net_revenuependiente

Paso 1 · tu consulta

SQL

Cargando el editor…

    ResultadoVista previa de products · presiona Ejecutar para ver el tuyo
    Vista previa de products
    product_idnamecategorysubcategoryunit_price
    101Vale Velvet LoveseatFurnitureSofas1659.00
    102Alder Oak Coffee TableFurnitureTables919.00
    103Orchard Rattan Dining ChairFurnitureChairs789.00
    104Upland Oak BookcaseFurnitureStorage429.00

    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 products · presiona Ejecutar para ver el tuyo
      Vista previa de products
      product_idnamecategorysubcategoryunit_price
      101Vale Velvet LoveseatFurnitureSofas1659.00
      102Alder Oak Coffee TableFurnitureTables919.00
      103Orchard Rattan Dining ChairFurnitureChairs789.00
      104Upland Oak BookcaseFurnitureStorage429.00

      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.

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

        Este Desafío final encadena varios joins. El truco es llevar la cuenta del grano en cada paso y saber qué joins pueden cambiar el conteo de filas.

        Paso 1: tres tablas, un grano

        Los ingresos viven en las líneas de pedido, así que order_items es la base. Cada línea tiene exactamente un pedido y un producto, así que unir orders y products añade columnas sin añadir filas. Es un join seguro: muchos a uno. Filtra por o.status e i.quantity, y luego agrupa por categoría.

        Paso 2: tasa de devolución con un LEFT JOIN

        Las devoluciones son opcionales: la mayoría de las líneas nunca se devuelven. Un LEFT JOIN de las líneas a returns conserva todas las líneas vendidas y rellena las columnas de devolución con NULL cuando no hay ninguna:

        LEFT JOIN returns AS r ON r.order_item_id = i.order_item_id

        Entonces COUNT(*) cuenta todas las líneas y COUNT(r.return_id) cuenta solo las que tienen una devolución. Como cada línea tiene como mucho una devolución en este conjunto de datos, este join no genera fan-out. En un sistema real donde una línea pudiera devolverse en dos paquetes, contarías DISTINCT i.order_item_id entre las líneas devueltas, o preagregarías las devoluciones por línea primero. Comprueba siempre: SELECT order_item_id, COUNT(*) FROM returns GROUP BY 1 HAVING COUNT(*) > 1 te lo dice en un segundo.

        Los filtros sobre o.status e i.quantity están en las tablas base, no en returns, así que dejarlos en WHERE es correcto. Un filtro como r.reason = 'damaged' en WHERE convertiría el LEFT JOIN en uno interno, como viste hace dos niveles. Ese pertenecería a ON.

        Una tasa de devolución es aquí una tasa de líneas. Megan podría querer también una tasa de unidades o de ingresos. Di cuál calculaste en tu respuesta.

        Paso 3: mejores clientes con nombres

        Agrupa por customer_id y también por las columnas de nombre que seleccionas. Agrupar solo por nombre fusionaría a dos clientes distintos que comparten un nombre, y Harbor & Pine tiene algunos registros de clientes duplicados, que desenredarás en el capítulo 7. Agrupa siempre por la clave y añade las columnas descriptivas a su lado.

        Comprobación final

        Suma los ingresos del paso 1 entre las categorías y compáralos con una consulta al estilo del paso 3 sin el LIMIT y sumada sobre todos los clientes. Ambos deben coincidir exactamente: son las mismas líneas agrupadas de dos formas. Si no coinciden, un join descartó o duplicó algo. Ese hábito de conciliación es lo que hace que la gente confíe en tus números.

        Errores comunes

        • Filtrar returns en WHERE. WHERE r.reason = 'damaged' descarta toda línea que no se devolvió, así que la tasa de devolución pasa a ser 100 %. Ponlo en el join: LEFT JOIN returns AS r ON r.order_item_id = i.order_item_id AND r.reason = 'damaged'.
        • Contar líneas devueltas con COUNT(*). Tras un LEFT JOIN eso cuenta todas las líneas. Usa COUNT(r.return_id), que omite los NULL de las líneas sin devolución.
        • Agrupar clientes solo por nombre. Dos clientes distintos que comparten un nombre se fusionan en uno. Agrupa por c.customer_id y añade las columnas de nombre a su lado.
        • Usar un inner join con returns. Conserva solo las líneas devueltas, así que el denominador se reduce a las propias devoluciones.

        En SQL Server

        ORDER BY net_revenue DESC, c.customer_id seguido de LIMIT 5 pasa a ser SELECT TOP 5 ... con el mismo ORDER BY. TOP 5 WITH TIES conserva todas las filas empatadas con la quinta, y OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY es la forma en SQL estándar.

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