Calcular venta diaria y total acumulado

Situación

Necesitamos una serie diaria de ventas y, junto a cada día, el importe acumulado desde el inicio del periodo. La primera cifra reduce partidas a una fila diaria; la segunda utiliza esas filas sin volver a reducirlas.

Primera aproximación

Intentar aplicar directamente una función de ventana sobre las partidas vuelve difícil distinguir el total del día del acumulado. Primero construiremos el nivel diario y después calcularemos la ventana.

Una función de ventana calcula un valor sobre filas relacionadas sin reunirlas en una sola. En SUM(...) OVER (...), OVER define esas filas; el marco ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW comienza en la primera fila ordenada y termina en la fila que se está leyendo.

Antes de formar la serie diaria, CONVERT(DATE, pe.FechaPedido) conserva la fecha y retira la parte de hora para que todos los pedidos del mismo día pertenezcan al mismo grupo.

Consulta final

DECLARE @Desde DATE = '20260301';
DECLARE @Hasta DATE = '20260401';

WITH VentasDiarias AS
(
    SELECT
        CONVERT(DATE, pe.FechaPedido) AS Fecha,
        SUM
        (
            d.Cantidad * d.PrecioUnitario
            * (1 - d.Descuento / 100.0)
        ) AS VentaDia
    FROM dbo.Pedido AS pe
    INNER JOIN dbo.PedidoDetalle AS d
        ON d.IdPedido = pe.IdPedido
    WHERE pe.Estado IN ('Confirmado', 'Enviado')
      AND pe.FechaPedido >= @Desde
      AND pe.FechaPedido <  @Hasta
    GROUP BY CONVERT(DATE, pe.FechaPedido)
)
SELECT
    v.Fecha,
    v.VentaDia,
    SUM(v.VentaDia) OVER
    (
        ORDER BY v.Fecha
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS VentaAcumulada
FROM VentasDiarias AS v
ORDER BY v.Fecha;

Recorrido

La CTE, una consulta con nombre disponible para la instrucción que sigue, filtra el periodo con un límite superior exclusivo y agrupa por fecha sin hora. VentaDia representa una fila por cada día que tuvo pedidos aceptados.

La segunda suma utiliza OVER: conserva esas filas y acumula desde la primera hasta la actual. Escribir explícitamente el marco ROWS deja claro qué filas participan y evita depender de comportamientos implícitos ante empates.

Resultado esperado

Fecha VentaDia VentaAcumulada
2026-03-01 728.00 728.00
2026-03-04 719.10 1447.10
2026-03-05 624.15 2071.25

Los días sin ventas no aparecen porque no existe una fila que agrupar.

Comprobación en SSMS

La cuadrícula permite seguir cómo cada venta diaria se incorpora al acumulado de la fila siguiente.

Resultado real en SSMS de la venta diaria acumulada

Variación: mostrar días en cero

Para una gráfica continua necesitamos una tabla calendario —una tabla con una fila por fecha y sus atributos—, o una secuencia de fechas controlada, y un LEFT JOIN contra VentasDiarias. Después convertimos la ausencia en cero mediante COALESCE.

También podemos agregar PARTITION BY para reiniciar el acumulado por mes, sucursal o moneda.

Límite o precaución

El acumulado solo es coherente si el orden es estable y las cifras comparten unidad. No sumamos monedas distintas ni mezclamos pedidos cancelados con ventas aceptadas.

Convertir la columna de fecha dentro del filtro puede dificultar el uso eficiente de un índice, una estructura que organiza valores para facilitar el acceso a filas. Por eso el WHERE compara la columna original con límites y reserva la conversión para agrupación y presentación.

Relacionado con

Índice del recorrido

Casos aplicados

10 artículos de Casos aplicados
  1. 01Consultar clientes con sus pedidos confirmados
  2. 02Encontrar clientes que todavía no tienen pedidos
  3. 03Mostrar el primer teléfono disponible de cada cliente
  4. 04Obtener ventas totales por cliente y filtrar las más altas
  5. 05Insertar un pedido y sus partidas de forma segura
  6. 06Actualizar precios de una categoría con validación previa
  7. 07Combinar pedidos actuales e históricos
  8. 08Obtener el último pedido de cada cliente
  9. 09Calcular venta diaria y total acumulado
  10. 10Construir una búsqueda paginada con filtros opcionales