1. Faro

Nivel 1 · Ingresos por categoría

¿Es nuevo para ti? Lee primero el concepto: Cómo un JOIN empareja filas

La solicitud

De Jessica Turner, Gerente de Merchandising

“¿Qué categorías traen el dinero? Me encantaría ver los ingresos netos por categoría en todos los pedidos, para planear las compras del próximo año.”

Tu objetivo

Une order_items con products por product_id. Considerando solo las líneas de pedido con quantity > 0 (todos los estados de pedido), devuelve category y net_revenue (suma de quantity * unit_price * (1 - discount_pct / 100.0) usando el unit_price de la línea de pedido, redondeada a 2), una fila por categoría.

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

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.

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

    Un join alinea filas de dos tablas que coinciden según una condición. Cada línea de pedido tiene un product_id, y cada producto tiene una fila en products, así que puedes adjuntar la categoría del producto a cada línea:

    SELECT i.order_item_id, i.quantity, p.name, p.category
    FROM order_items AS i
    JOIN products AS p ON p.product_id = i.product_id;

    JOIN a secas significa INNER JOIN: solo se conservan las filas que encuentran coincidencia en ambos lados. Una línea de pedido cuyo producto no esté en products desaparecería. En Harbor & Pine todas las líneas tienen un producto válido, pero en los almacenes de datos reales eso nunca está garantizado, por eso importa el siguiente nivel.

    Alias y nombres calificados

    order_items AS i le da a la tabla un nombre corto. Cuando una consulta toca varias tablas, antepón a cada columna el alias de su tabla (i.quantity, p.category). Es obligatorio cuando ambas tablas tienen una columna con el mismo nombre. Aquí ambas tienen unit_price: i.unit_price es el precio pagado, mientras que p.unit_price es el precio de lista de hoy. Usar la equivocada es un error real y común de ingresos. Escribir unit_price sin prefijo falla con un error de “columna ambigua”, que es la base de datos haciéndote un favor.

    Primero el join, luego agregar

    Los joins ocurren primero (son parte de FROM), luego WHERE, luego GROUP BY. Así que puedes filtrar y agrupar por columnas de cualquiera de las dos tablas:

    SELECT p.category,
           ROUND(SUM(i.quantity * i.unit_price * (1 - i.discount_pct / 100.0)), 2) AS net_revenue
    FROM order_items AS i
    JOIN products AS p ON p.product_id = i.product_id
    WHERE i.quantity > 0
    GROUP BY p.category;

    Comprueba el conteo de filas

    El mejor hábito con los joins: saber cuántas filas esperas y comprobarlo. Unir líneas de pedido con productos debería devolver exactamente una fila por línea de pedido, porque cada línea coincide con un producto. Compara SELECT COUNT(*) FROM order_items con el conteo tras el join. Si el conteo del join es mayor, el lado “uno” tenía duplicados. Si es menor, algunas líneas no tenían coincidencia.

    Variaciones de sintaxis

    • USING (product_id) es un atajo de ON a.product_id = b.product_id cuando los nombres de columna son idénticos. Funciona en Postgres, DuckDB, MySQL y BigQuery.
    • Los joins con coma al estilo antiguo, FROM order_items i, products p WHERE p.product_id = i.product_id, dan el mismo resultado, pero olvidar la condición del WHERE crea un cross join con todas las combinaciones de filas. Escribe mejor JOIN ... ON explícito.
    • NATURAL JOIN coincide por todas las columnas con el mismo nombre, lo que aquí incluiría unit_price y descartaría en silencio la mayoría de las filas. Evítalo.

    Nota de entrevista

    Prepárate para explicar INNER vs LEFT JOIN con un ejemplo concreto, y para decir qué pasa con las filas sin coincidencia. “Un inner join las descarta” es la esencia.

    Errores comunes

    • Dejar una columna compartida sin calificar, o elegir la copia equivocada. unit_price a secas es ambiguo, y p.unit_price es el precio de lista de hoy. Los ingresos necesitan el precio pagado: i.unit_price.
    • Olvidar la condición del join. Un join con coma sin su WHERE, FROM order_items i, products p, empareja cada línea con cada producto. Escribe JOIN products AS p ON p.product_id = i.product_id.
    • Unir por la clave equivocada. ON p.product_id = i.order_item_id se ejecuta sin error, porque ambas columnas son números, y adjunta categorías al azar. Haz coincidir la clave con la columna que se refiere a ella.
    • Saltarse la comprobación del conteo de filas. Si el conteo del join difiere de SELECT COUNT(*) FROM order_items, se descartaron o duplicaron líneas incluso antes de agrupar.

    En SQL Server

    Los joins se escriben exactamente igual. Dos hábitos que verás en SSMS: las tablas llevan un prefijo de esquema (Sales.SalesOrderHeader), que las tablas de Harbor & Pine no necesitan, y para echar un vistazo a las filas se usa SELECT TOP 10 * FROM ... en lugar de LIMIT 10.

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