1. Faro

Nivel 3 · Calcula el margen por producto

La solicitud

De Brian Mitchell, Analista de Finanzas

“¿Puedes conseguirme el margen de cada producto? Necesito el margen en dólares por unidad y el margen como porcentaje del precio, para detectar los artículos con margen bajo.”

Tu objetivo

Desde products, devuelve product_id, name, unit_margin (unit_price menos unit_cost, redondeado a 2 decimales) y margin_pct (unit_margin dividido entre unit_price por 100, redondeado a 1 decimal). Incluye todos los productos; los productos sin costo mostrarán márgenes NULL, lo cual es lo esperado.

  • Lee de la tabla productspendiente
  • Muestra exactamente product_id name unit_margin margin_pctpendiente
  • 120 filaspendiente

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

    La lista de SELECT no se limita a columnas que ya existen. Sirve cualquier expresión: aritmética, funciones o constantes. Los analistas hacen esto constantemente para derivar métricas como el margen, el impuesto o el precio por unidad.

    SELECT
      name,
      unit_price,
      unit_price * 1.08 AS price_with_tax
    FROM products;

    Alias con AS

    AS price_with_tax le da un nombre a la columna calculada, llamado alias. Sin él, la base de datos inventa un encabezado como (unit_price * 1.08), que queda feo en una hoja de cálculo y es imposible de referenciar limpiamente después. Usa alias en snake_case y minúsculas que describan la métrica. AS es opcional en la mayoría de las bases de datos (unit_price * 1.08 price_with_tax también funciona), pero escribirlo hace que la intención sea evidente y evita el error de la coma que falta del nivel anterior.

    Redondeo

    ROUND(x, 2) redondea a dos decimales. Redondea al final del todo, sobre el número final. Si redondeas un valor intermedio y luego divides, el error se acumula:

    -- good: compute from raw values, round once
    ROUND((unit_price - unit_cost) / unit_price * 100, 1) AS margin_pct

    NULL en la aritmética

    Algunos productos no tienen unit_cost registrado. Cualquier operación aritmética con NULL devuelve NULL: 49.99 - NULL es NULL, no 49.99. Normalmente es lo que quieres aquí: un costo desconocido significa un margen desconocido, y reportarlo como un margen del 100 % sería incorrecto. Aprenderás a reemplazar los NULL de forma deliberada con COALESCE en el capítulo 6, pero hazlo solo cuando un valor por defecto tenga sentido de verdad.

    División entera

    En este conjunto de datos los precios son decimales, así que la división se comporta como esperas. Ten cuidado con las columnas enteras. En Postgres, 7 / 2 devuelve 3 porque ambos lados son enteros. DuckDB y MySQL devuelven 3.5, y BigQuery requiere DIV() para la división entera. Un hábito portable y seguro al dividir conteos es multiplicar antes por 1.0, o convertir el tipo: CAST(a AS DECIMAL) / b.

    Errores comunes

    • Olvidar los paréntesis: unit_price - unit_cost / unit_price divide primero y después resta. La multiplicación y la división tienen prioridad sobre la suma y la resta, igual que en la clase de matemáticas.
    • Referirse a un alias en la misma lista de SELECT: SELECT unit_price - unit_cost AS m, m / unit_price falla en Postgres, MySQL y BigQuery. DuckDB lo permite, pero conviene repetir la expresión o usar una subconsulta para que tu SQL funcione en todas partes.

    En SQL Server

    La aritmética, AS y ROUND funcionan igual. Dos diferencias te pueden pillar: un entero dividido entre un entero se trunca (7 / 2 es 3), y el texto se une con + o CONCAT en lugar de ||:

    SELECT Name + ' (' + Color + ')' AS Label,
           ROUND((ListPrice - StandardCost) / ListPrice * 100, 1) AS MarginPct
    FROM Production.Product;

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