Problemas al calcular saldo acumulado en consulta access

Tengo dos tablas, tblProductos, tblDetalleMov, relacionadas entre si, tengo una consulta C_DetalleMov, con ambas tablas, quiero añadirle un campo Saldo, que me calcule el acumulado histórico de cada Producto Ingresado, he probado diferentes métodos, pero no me funciona

3 Respuestas

Respuesta
1

Mejor utilizar funciones de Access 'embebidas' en la consulta.

Ejemplo en:

https://www.accessaplicaciones.com/ejemplos.html#sq03

Respuesta
1

Crear una consulta basada en la tabla [tblDetalleMov] que agrupe por producto y sume/reste sus movimientos, si los datos son fieles: el saldo será real.
La consulta debería devolver el ID de cada producto [ID_Producto] y el campo antes calculado (su [Saldo]).
Añade la consulta (se la podría llamar [SALDOxProducto]) a la actual consulta –como- si fuera una tabla más) y relaciónalas por el campo [ID_Producto], añade (a la actual consulta) el campo [Saldo] y debería presentar datos coherentes con los movimientos guardados.

Respuesta
1

Dado que Access no tiene funciones de ventana como PostgreSQL, hay que usar un truco con subconsultas correlacionadas o aprovechar el motor de SQL para acumular datos por producto y fecha.

Supuestos:
Tienes algo como:

TblProductos

IDProducto (clave primaria)

NombreProducto

TblDetalleMov

IDMov (clave primaria)

IDProducto (foránea a tblProductos)

FechaMov

Cantidad (positiva para ingreso, negativa para salida)

Objetivo
Agregar un campo Saldo a la consulta C_DetalleMov que muestre el acumulado histórico de cada producto ordenado por fecha.

Solución:

Usar una subconsulta correlacionada
La forma más efectiva en Access es usar una subconsulta en SQL. Aquí tienes un ejemplo:

SELECT 
    D1. IDProducto,
    D1. FechaMov,
    D1. Cantidad,
    (
        SELECT SUM(D2.Cantidad)
        FROM tblDetalleMov AS D2
        WHERE D2.IDProducto = D1.IDProducto
          AND D2.FechaMov <= D1.FechaMov
          AND (D2.IDMov <= D1.IDMov OR D2.FechaMov < D1.FechaMov)
    ) AS Saldo
FROM tblDetalleMov AS D1
ORDER BY D1. IDProducto, D1. FechaMov, D1.IDMov;

Explicación
Esta subconsulta suma todas las cantidades de movimientos anteriores o iguales (por fecha e ID) al movimiento actual.

El AND D2.IDMov <= D1. IDMov OR D2. FechaMov < D1. FechaMov asegura que si hay varios movimientos el mismo día, se use el IDMov como criterio secundario para evitar duplicación en el saldo.

Consideraciones:
Si tienes muchos datos, esta consulta puede volverse lenta, ya que Access recalcula el saldo para cada fila.

Para mejorar rendimiento, puedes usar consultas de acción para almacenar saldos en una tabla intermedia, actualizándolos cuando sea necesario.

Alternativa con VBA (si lo necesitas)
Si el rendimiento es crítico o necesitas mayor control, puedes hacerlo con código VBA recorriendo los datos y actualizando un campo Saldo en una tabla.

Aquí tienes una solución con VBA en Access para calcular el saldo acumulado por producto y guardarlo en una tabla.

Supuestos de estructura:
Tabla de movimientos: tblDetalleMov

IDMov (clave primaria, autonumérico)

IDProducto (clave foránea)

FechaMov (fecha del movimiento)

Cantidad (positiva = ingreso, negativa = salida)

Saldo (nuevo campo a actualizar)

Campo Saldo ya existe en tblDetalleMov. Si no existe, agrégalo:

Tipo: Número (doble)

Nombre: Saldo

Código VBA para calcular y actualizar el saldo acumulado

Sub CalcularSaldoAcumulado()
    Dim db As DAO.Database
    Dim rsProductos As DAO.Recordset
    Dim rsMovimientos As DAO.Recordset
    Dim saldo As Double
    Dim strSQL As String
    Set db = CurrentDb
    ' Obtener todos los productos
    Set rsProductos = db.OpenRecordset("SELECT IDProducto FROM tblProductos", dbOpenSnapshot)
    Do While Not rsProductos.EOF
        saldo = 0
        ' Obtener los movimientos para el producto actual, ordenados por fecha e IDMov
        strSQL = "SELECT * FROM tblDetalleMov " & _
                 "WHERE IDProducto = " & rsProductos!IDProducto & " " & _
                 "ORDER BY FechaMov, IDMov"
        Set rsMovimientos = db.OpenRecordset(strSQL, dbOpenDynaset)
        Do While Not rsMovimientos.EOF
            saldo = saldo + rsMovimientos!Cantidad
            rsMovimientos.Edit
            rsMovimientos!Saldo = saldo
            rsMovimientos.Update
            rsMovimientos.MoveNext
        Loop
        rsMovimientos.Close
        rsProductos.MoveNext
    Loop
    rsProductos.Close
    Set rsMovimientos = Nothing
    Set rsProductos = Nothing
    Set db = Nothing
    MsgBox "Saldos acumulados actualizados correctamente.", vbInformation
End Sub

Cómo usarlo:
Abre el Editor de VBA (Alt + F11).

Inserta un módulo nuevo (Insert > Module).

Pega el código anterior.

Cierra el editor.

En Access, ejecuta la macro desde la ventana de macros o el panel de VBA (F5 en el módulo).

Verifica que el campo Saldo en tblDetalleMov se haya actualizado correctamente.

Si quieres algo más profesional te invito a tomar mi clase sobre Access con PostgreSQL, con esto supera las limitaciones de Access. Me puedes contactar en [email protected]

¡Gracias! 

He utilizado la función de Jesús y tiene ventajas y desventajas

Ventajas:

Más rápida que la subconsulta si hay muchos registros.

Muy ingeniosa solución "en fila" (tipo "running total").

Desventajas y problemas reales:

La variable Static puede no reiniciarse correctamente si se ejecuta la consulta más de una vez o en entornos multiusuario o multiinstancia.

Si se utiliza desde un formulario o informe, o se vuelve a ejecutar en una sesión nueva, puede dar resultados incorrectos (la suma sigue desde un estado anterior).

No garantiza reproducibilidad de resultados sin reiniciar el entorno VBA.

Un saludo para el maestro Jesus Mansilla Castells

Añade tu respuesta

Haz clic para o

Más respuestas relacionadas