1. Faro

Nivel 3 · Ingresos acumulados del año

La solicitud

De Brian Mitchell, Analista Financiero

“Junto a los ingresos mensuales de 2025, quiero una columna del acumulado del año para que la junta vea cómo vamos frente a la meta anual al cierre de cada mes.”

Tu objetivo

Para los pedidos completados realizados en 2025 y las líneas con quantity > 0, devuelve una fila por mes con: month (primer día del mes, como DATE), net_revenue (suma de quantity * unit_price * (1 - discount_pct / 100.0) del mes, redondeada a 2) y ytd_revenue (total acumulado de los ingresos netos mensuales desde enero hasta ese mes, redondeado a 2). Ordena por month ascendente.

  • Lee de la tabla orderspendiente
  • Lee de la tabla order_itemspendiente
  • Muestra exactamente month net_revenue ytd_revenuependiente

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.

    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 agregados como SUM, AVG, COUNT, MIN y MAX también pueden usarse como funciones de ventana. Con un ORDER BY dentro de OVER, se vuelven acumulativos:

    SUM(net_revenue) OVER (ORDER BY month) AS ytd_revenue

    Para cada mes, esto suma los ingresos de todos los meses hasta él, incluido: el acumulado de enero es enero, el de febrero es enero más febrero, y así sucesivamente. Las filas no se colapsan, así que obtienes el número mensual y el total acumulado lado a lado.

    Primero agrega, luego la ventana

    Los ingresos viven en las líneas de pedido, pero el total acumulado es sobre meses. Agrega a una fila por mes en una CTE y aplica la ventana encima. Si pones la ventana directamente sobre filas a nivel de línea, obtendrías un total acumulado por línea, que no es lo que nadie quiere en una diapositiva.

    Marcos: qué significa “hasta la fila actual”

    Toda ventana con ORDER BY tiene un marco: el conjunto de filas que la función ve para la fila actual. El marco por defecto cuando escribes solo ORDER BY es:

    RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

    RANGE trata las filas con el mismo valor de ORDER BY como pares e incluye a todas. Con una fila por mes eso no cambia nada. Pero si hicieras una suma acumulada sobre pedidos ordenados por fecha y dos pedidos compartieran fecha, ambos mostrarían el mismo total acumulado, incluyéndose mutuamente. Eso sorprende a la gente. Escribir el marco explícitamente con ROWS lo hace fila por fila:

    SUM(net_revenue) OVER (ORDER BY month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)

    Ser explícito no cuesta nada y documenta la intención. Este marco por defecto se aplica por igual en Postgres, DuckDB, MySQL 8, BigQuery y Snowflake.

    Reiniciar el total

    PARTITION BY reinicia el total acumulado por grupo. Para tener un acumulado del año que se reinicia cada año con datos de varios años:

    SUM(net_revenue) OVER (
      PARTITION BY EXTRACT(YEAR FROM month)
      ORDER BY month
      ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS ytd_revenue

    O por canal, por categoría, por cliente: lo que el negocio siga por separado.

    Participación sobre el total

    La misma función con un OVER () vacío da el total general en cada fila, que es la forma estándar de calcular la participación sobre el total sin una subconsulta:

    ROUND(100.0 * net_revenue / SUM(net_revenue) OVER (), 1) AS pct_of_year

    Errores comunes

    • Olvidar el ORDER BY dentro de OVER: SUM(x) OVER () es el total general, no un total acumulado.
    • Confiar en el ORDER BY externo. El externo ordena la salida; solo el ORDER BY dentro de OVER define el orden de acumulación.
    • Redondear los valores mensuales antes de sumarlos, lo que hace que el acumulado se desvíe unos centavos del total real.

    En SQL Server

    La ventana es la misma, pero escribe el marco completo: ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. Con solo ORDER BY, el marco por defecto de SQL Server es RANGE, que allí es más lento y suma los meses empatados en un solo paso. Reinicia por año con PARTITION BY YEAR(month).

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