Saltar al contenido principal

Lenguaje de Consulta de Beancount - Consultas Financieras tipo SQL

Beancount cuenta con un potente Lenguaje de Consulta (BQL) tipo SQL que te permite segmentar, analizar y examinar tus datos financieros con precisión. Ya sea que quieras generar un informe rápido, depurar una entrada o realizar análisis complejos, dominar BQL es clave para desbloquear todo el potencial de tu libro de contabilidad en texto plano. Esta guía te guiará a través de su estructura, funciones y mejores prácticas. 🔍


Estructura y Ejecución de Consultas

El núcleo de BQL es su sintaxis familiar, inspirada en SQL. Las consultas se ejecutan utilizando la herramienta de línea de comandos bean-query, que procesa tu archivo de libro mayor y devuelve los resultados directamente en tu terminal.

Formato Básico de Consulta

Una consulta BQL se compone de tres cláusulas principales: SELECT, FROM y WHERE.

SELECT <objetivo1>, <objetivo2>, ...
FROM <expresión-filtro-entrada>
WHERE <expresión-filtro-apunte>;
  • SELECT: Especifica qué columnas de datos deseas recuperar.
  • FROM: Filtra transacciones enteras antes de que se procesen.
  • WHERE: Filtra las líneas de los apuntes individuales después de que se haya seleccionado la transacción.

Sistema de Filtrado de Dos Niveles

Comprender la diferencia entre las cláusulas FROM y WHERE es crucial para escribir consultas precisas. BQL utiliza un proceso de filtrado de dos niveles.

  1. Nivel de Transacción (FROM) Esta cláusula actúa sobre transacciones enteras. Si una transacción coincide con la condición FROM, la transacción entera (incluidos todos sus apuntes) se pasa a la siguiente etapa. Esta es la forma principal de filtrar datos, ya que preserva la integridad del sistema de contabilidad de doble entrada. Por ejemplo, filtrar FROM year = 2024 selecciona todas las transacciones que ocurrieron en 2024.

  2. Nivel de Apunte (WHERE) Esta cláusula filtra los apuntes individuales dentro de las transacciones seleccionadas por la cláusula FROM. Esto es útil para la presentación y para centrarse en tramos específicos de una transacción. Sin embargo, ten en cuenta que filtrar a este nivel puede "romper" la integridad de una transacción en la salida, ya que solo podrías ver un lado de una entrada. Por ejemplo, podrías seleccionar todos los apuntes a tu cuenta Expenses:Groceries.


Modelo de Datos

Para consultar tus datos de manera efectiva, necesitas entender cómo Beancount los estructura. Un libro mayor es una lista de directivas, pero BQL se enfoca principalmente en las entradas Transaction.

Estructura de la Transacción

Cada Transaction es un contenedor con atributos de nivel superior y una lista de objetos Posting.

Transaction
├── date
├── flag
├── payee
├── narration
├── tags
├── links
└── Postings[]
├── account
├── units
├── cost
├── price
└── metadata

Tipos de Columnas Disponibles

Puedes SELECT cualquiera de los atributos de la transacción o de sus apuntes.

  1. Atributos de Transacción Estas columnas son las mismas para cada apunte dentro de una sola transacción.

    SELECT
    date, -- La fecha de la transacción (datetime.date)
    year, -- El año de la transacción (int)
    month, -- El mes de la transacción (int)
    day, -- El día de la transacción (int)
    flag, -- El indicador de transacción, p. ej., "*" o "!" (str)
    payee, -- El beneficiario (str)
    narration, -- La descripción o memo (str)
    tags, -- Un conjunto de etiquetas, p. ej., #viaje-2024 (set[str])
    links -- Un conjunto de enlaces, p. ej., ^informe-de-gastos (set[str])
  2. Atributos del Apunte Estas columnas son específicas de cada línea de apunte individual.

    SELECT
    account, -- El nombre de la cuenta (str)
    position, -- El importe total, incluyendo unidades y coste (Position)
    units, -- El número y la moneda del apunte (Amount)
    cost, -- La base del coste del apunte (Cost)
    price, -- El precio utilizado en el apunte (Amount)
    weight, -- La posición convertida a su base de coste (Amount)
    balance -- El total acumulado de unidades en la cuenta (Inventory)

Funciones de Consulta

BQL incluye un conjunto de funciones para la agregación y transformación de datos, muy similar a SQL.

Funciones de Agregación

Las funciones de agregación resumen los datos en múltiples filas. Cuando se utilizan con GROUP BY, proporcionan resúmenes agrupados.

-- Cuenta el número de apuntes
SELECT COUNT(*)

-- Suma el valor de todos los apuntes (convertidos a una moneda común)
SELECT SUM(position)

-- Encuentra la fecha de la primera y la última transacción
SELECT FIRST(date), LAST(date)

-- Encuentra los valores mínimo y máximo de la posición
SELECT MIN(position), MAX(position)

-- Agrupa por cuenta para obtener una suma para cada una
SELECT account, SUM(position) GROUP BY account

Funciones de Posición/Inventario

La columna position es un objeto compuesto. Estas funciones te permiten extraer partes específicas de él o calcular su valor de mercado.

-- Extrae solo el número y la moneda de una posición
SELECT UNITS(position)

-- Muestra el coste total de una posición
SELECT COST(position)

-- Muestra la posición convertida a su base de coste (útil para inversiones)
SELECT WEIGHT(position)

-- Calcula el valor de mercado utilizando los datos de precio más recientes
SELECT VALUE(position)

Puedes combinar estas para obtener informes potentes. Por ejemplo, para ver el coste total y el valor de mercado actual de tu cartera de inversión:

SELECT
account,
COST(SUM(position)) AS total_cost,
VALUE(SUM(position)) AS market_value
FROM
account ~ "Assets:Investments"
GROUP BY
account

Características Avanzadas

Más allá de las sentencias básicas SELECT, BQL ofrece comandos especializados para informes financieros comunes.

Informes de Saldos

La sentencia BALANCES genera un balance general o un estado de resultados para un período específico.

-- Genera un balance general simple al inicio de 2024
BALANCES FROM close ON 2024-01-01
WHERE account ~ "^Assets|^Liabilities"

-- Genera un estado de resultados para el año fiscal 2024
BALANCES FROM
OPEN ON 2024-01-01
CLOSE ON 2024-12-31
WHERE account ~ "^Income|^Expenses"

Informes de Diario

La sentencia JOURNAL muestra la actividad detallada de una o más cuentas, de forma similar a una vista de libro mayor tradicional.

-- Muestra toda la actividad en tu cuenta corriente a su coste original
JOURNAL "Assets:Checking" AT COST

-- Muestra todas las transacciones 401k, mostrando solo las unidades (acciones)
JOURNAL "Assets:.*:401k" AT UNITS

Operaciones de Impresión

La sentencia PRINT es una herramienta de depuración que muestra las transacciones completas coincidentes en su formato de archivo Beancount original.

-- Imprime todas las transacciones relacionadas con inversiones de 2024
PRINT FROM year = 2024
WHERE account ~ "Assets:Investments"

-- Encuentra una transacción por su ID único (generado por algunas herramientas)
PRINT FROM id = "8e7c47250d040ae2b85de580dd4f5c2a"

Expresiones de Filtrado

Puedes crear filtros sofisticados utilizando operadores lógicos (AND, OR), expresiones regulares (~) y comparaciones.

-- Encuentra todos los gastos de viaje de la segunda mitad de 2024
SELECT * FROM
year = 2024 AND month >= 6
WHERE account ~ "Expenses:Travel"

-- Encuentra todas las transacciones relacionadas con unas vacaciones o un negocio
SELECT * FROM
"vacation-2024" IN tags OR
"business-trip" IN links

Consideraciones de Rendimiento ⚙️

bean-query está diseñado para la eficiencia, pero comprender su flujo operativo puede ayudarte a escribir consultas más rápidas en libros mayores grandes.

  1. Carga de Datos: Beancount primero analiza todo tu archivo de libro mayor y ordena todas las transacciones cronológicamente. Todo este conjunto de datos se guarda en la memoria.
  2. Optimización de Consultas: El motor de consultas aplica filtros en un orden específico para la máxima eficiencia: FROM (transacciones) -> WHERE (apuntes) -> Agregaciones. Filtrar en el nivel FROM es más rápido porque reduce el conjunto de datos al principio.
  3. Uso de Memoria: Todas las operaciones se realizan en memoria. Los objetos Position y las agregaciones de Inventory están optimizados, pero los conjuntos de resultados muy grandes pueden consumir una cantidad significativa de RAM. BQL no utiliza almacenamiento temporal basado en disco.

Mejores Prácticas

Sigue estos consejos para escribir consultas limpias, efectivas y fáciles de mantener.

  1. Organización de Consultas Formatea tus consultas para facilitar la lectura, especialmente las complejas. Utiliza saltos de línea y sangría para separar las cláusulas.

    -- Una consulta limpia y legible para todos los gastos de 2024
    SELECT
    date,
    account,
    position
    FROM
    year = 2024
    WHERE
    account ~ "Expenses"
    ORDER BY
    date DESC;
  2. Depuración Si una consulta no funciona como se espera, utiliza EXPLAIN para ver cómo Beancount la analiza. Para probar un filtro, utiliza SELECT DISTINCT para ver qué valores únicos coinciden.

    -- Ver el plan de consulta
    EXPLAIN SELECT date, account, position;

    -- Prueba qué cuentas coinciden con una expresión regular
    SELECT DISTINCT account
    WHERE account ~ "^Assets:.*";
  3. Aserciones de Saldo Puedes usar BQL para verificar las aserciones de balance en tu libro mayor. Esta consulta debería devolver la cantidad exacta especificada en tu última comprobación de saldo para esa cuenta.

    -- Verifica el saldo final de tu cuenta corriente
    SELECT account, sum(position)
    FROM close ON 2025-01-01 -- Utiliza la fecha de tu directiva de saldo
    WHERE account = "Assets:Checking";