Beancount incluye un potente lenguaje de consultas tipo SQL (BQL) que te permite segmentar, combinar y analizar tus datos financieros con precisión. Ya sea que quieras generar un informe rápido, depurar un asiento o realizar análisis complejos, dominar BQL es clave para desbloquear todo el potencial de tu libro contable en texto plano. Esta guía te mostrará su estructura, funciones y buenas prácticas. 🔍

Explora el libro contable en vivo →
Estructura de Consultas y Ejecución
El núcleo de BQL es su sintaxis familiar inspirada en SQL. Ejecuta consultas con bea query: bea --file <ledger> query "SELECT …" imprime la tabla en tu terminal, y bea query sin argumentos abre el shell interactivo. Cada consulta de esta guía se ejecutó con Beancount 3.2.3 y beanquery 0.2.0.
Formato Básico de Consulta
Una consulta BQL se compone de tres cláusulas principales: SELECT, FROM y WHERE.
SELECT <target1>, <target2>, ...
FROM <entry-filter-expression>
WHERE <posting-filter-expression>;SELECT: Especifica qué columnas de datos quieres recuperar.FROM: Filtra transacciones completas antes de que se procesen.WHERE: Filtra las líneas de asiento individuales después de que se haya seleccionado la transacción.
Sistema de Filtrado de Dos Niveles
Entender la diferencia entre las cláusulas FROM y WHERE es crucial para escribir consultas precisas. BQL utiliza un proceso de filtrado en dos niveles.
-
Nivel de transacción (
FROM) Esta cláusula actúa sobre transacciones completas. Si una transacción coincide con la condiciónFROM, la transacción completa (incluyendo todos sus asientos) pasa a la siguiente etapa. Esta es la forma principal de filtrar datos, ya que preserva la integridad del sistema de contabilidad por partida doble. Por ejemplo, filtrarFROM year = 2024selecciona todas las transacciones que ocurrieron en 2024. -
Nivel de asiento (
WHERE) Esta cláusula filtra los asientos individuales dentro de las transacciones seleccionadas por la cláusulaFROM. Esto es útil para la presentación y para enfocarse en piernas específicas de una transacción. Sin embargo, ten en cuenta que filtrar en este nivel puede "romper" la integridad de una transacción en la salida, ya que podrías ver solo un lado de un asiento. Por ejemplo, podrías seleccionar todos los asientos de tu cuentaExpenses:Groceries.
Concretamente, PRINT FROM year = 2024 devuelve transacciones completas (ambas piernas de cada asiento), mientras que SELECT date, narration, account, position FROM year = 2024 WHERE account ~ "Assets:Broker" devuelve una fila por cada asiento que coincida. En un libro contable con dos compras de corretaje, la primera consulta devuelve los asientos completos y la segunda devuelve exactamente las dos filas del corretaje.
Modelo de Datos
Para consultar tus datos de manera efectiva, necesitas entender cómo Beancount los estructura. Un libro contable es una lista de directivas, pero BQL se enfoca principalmente en los asientos Transaction.
Estructura de 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
└── metadataTipos de Columnas Disponibles
Puedes hacer SELECT de cualquiera de los atributos de la transacción o de sus asientos.
-
Atributos de transacción Estas columnas son las mismas para cada asiento dentro de una misma transacción.
SELECT date, -- The date of the transaction (datetime.date) year, -- The year of the transaction (int) month, -- The month of the transaction (int) day, -- The day of the transaction (int) flag, -- The transaction flag, e.g., "*" or "!" (str) payee, -- The payee (str) narration, -- The description or memo (str) tags, -- A set of tags, e.g., #trip-2024 (set[str]) links -- A set of links, e.g., ^expense-report (set[str]) -
Atributos de asiento Estas columnas son específicas de cada línea de asiento individual.
SELECT account, -- The account name (str) position, -- The full amount, including units and cost (Position) units(position), -- The number and currency of the posting (Amount) cost(position), -- The cost basis of the posting (Amount) price, -- The price used in the posting (Amount) weight, -- The position converted to its cost basis (Amount) balance -- The running total of units in the account (Inventory)unitsycostson funciones que recibenposition.price,weightybalanceson columnas simples.
Funciones de Consulta
BQL incluye un conjunto de funciones para agregación y transformación de datos, muy parecido a SQL.
Funciones de Agregación
Las funciones de agregación resumen datos a través de múltiples filas. Cuando se usan con GROUP BY, proporcionan resúmenes agrupados.
-- Count the number of postings
SELECT COUNT(*)
-- Sum all postings into one Inventory; currencies and lots are kept, not converted
SELECT SUM(position)
-- one row, e.g. (-2300.00 USD, 10 HOOL {150.00 USD, 2024-09-05}, 5 HOOL {160.00 USD, 2024-11-02})
-- Total one account in a single currency explicitly (positions without a price keep their currency)
SELECT SUM(CONVERT(position, 'USD')) WHERE account ~ "Assets:Checking"
-- one row, e.g. (2580.00 USD)
-- Find the date of the first and last transaction
SELECT FIRST(date), LAST(date)
-- Find the minimum and maximum position values
SELECT MIN(position), MAX(position)
-- Group by account to get a sum for each
SELECT account, SUM(position) GROUP BY accountFunciones de Posición/Inventario
La columna position es un objeto compuesto. Estas funciones te permiten extraer partes específicas de ella o calcular su valor de mercado.
-- Extract just the number and currency from a position
SELECT UNITS(position)
-- Show the total cost of a position
SELECT COST(position)
-- Show each posting at its cost value (a column; there is no WEIGHT() function)
SELECT account, weight WHERE account ~ "Assets:Investments"
-- Calculate the market value using the latest price data
-- (needs a price directive for the holding; otherwise the position is returned unchanged)
SELECT VALUE(position)Puedes combinarlas para generar informes potentes. Por ejemplo, para ver el costo total y el valor de mercado actual de tu cartera de inversiones. El valor de mercado necesita una directiva price para cada posición (por ejemplo 2024-12-01 price HOOL 175.00 USD). Ambos agregados devuelven un Inventory por cuenta, así que cada divisa sigue listándose por separado.
SELECT
account,
COST(SUM(position)) AS total_cost,
VALUE(SUM(position)) AS market_value
FROM
account ~ "Assets:Investments"
GROUP BY
account
-- one row per account, e.g. Assets:Broker:HOOL | (2300.00 USD) | (2625.00 USD)Las directivas de precio que usan las consultas de valor de mercado pueden provenir de asientos manuales, un obtentor de cotizaciones local o Precios en vivo en un cargador compatible. Los feeds gestionados no cambian la sintaxis de las consultas. Las herramientas locales ascendentes necesitan archivos de precios locales, y las consultas históricas siguen necesitando precios en la fecha solicitada o antes.
Funciones Avanzadas
Más allá de las sentencias SELECT básicas, BQL ofrece comandos especializados para informes financieros habituales.
Informes de Balance
La sentencia BALANCES genera un balance general o una cuenta de resultados para un período específico.
-- Generate a simple balance sheet as of the start of 2024
BALANCES FROM close ON 2024-01-01
WHERE account ~ "^Assets|^Liabilities"
-- Generate an income statement for the 2024 fiscal year
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, similar a una vista de libro mayor tradicional.
-- Show all activity in your checking account at its original cost
JOURNAL "Assets:Checking" AT COST
-- Show all 401k transactions, displaying only the units (shares)
JOURNAL "Assets:.*:401k" AT UNITSOperaciones de Impresión
La sentencia PRINT es una herramienta de depuración que muestra transacciones completas coincidentes en su formato original del archivo de Beancount. Solo acepta un filtro de asiento. Una cláusula WHERE aquí es un error de sintaxis. Para reducir la salida a una pierna de cada asiento, usa SELECT con un filtro de asiento en su lugar. Devuelve una fila por cada asiento que coincida.
-- Print all 2024 transactions in full (every posting of each matching entry)
PRINT FROM year = 2024
-- Show only the investment postings of 2024 transactions
SELECT date, narration, account, position
FROM year = 2024
WHERE account ~ "Assets:Investments"
-- Find a transaction by its unique ID (generated by some tools)
-- Returns the matching entry, or no rows when nothing carries that ID
PRINT FROM id = "8e7c47250d040ae2b85de580dd4f5c2a"Expresiones de Filtrado
Puedes construir filtros sofisticados usando operadores lógicos (AND, OR), expresiones regulares (~) y comparaciones.
Los literales de cadena usan comillas simples. Las comillas dobles delimitan la expresión regular después de ~.
-- Find all travel expenses from the second half of 2024
-- SELECT * returns date, flag, payee, narration and position per matching posting
SELECT * FROM
year = 2024 AND month >= 6
WHERE account ~ "Expenses:Travel"
-- Find all transactions related to a vacation or business
SELECT * FROM
'vacation-2024' IN tags OR
'business-trip' IN linksConsideraciones de Rendimiento ⚙️
bea query está diseñado para ser eficiente, pero entender su flujo operativo puede ayudarte a escribir consultas más rápidas en libros contables grandes.
- Carga de datos: Beancount primero analiza todo tu archivo de libro contable y ordena todas las transacciones cronológicamente. Todo este conjunto de datos se mantiene en memoria.
- Optimización de consultas: El motor de consultas aplica los filtros en un orden específico para máxima eficiencia:
FROM(transacciones) ->WHERE(asientos) -> Agregaciones. Filtrar a nivel deFROMes lo más rápido porque reduce el conjunto de datos desde el principio. - Uso de memoria: Todas las operaciones ocurren en memoria. Los objetos
Positiony las agregacionesInventoryestán optimizados, pero conjuntos de resultados muy grandes pueden consumir una cantidad significativa de RAM. BQL no usa almacenamiento temporal en disco.
Mejores Prácticas
Sigue estos consejos para escribir consultas limpias, efectivas y mantenibles.
-
Organización de consultas Formatea tus consultas para facilitar su lectura, especialmente las complejas. Usa saltos de línea y sangría para separar las cláusulas.
-- A clean, readable query for all 2024 expenses SELECT date, account, position FROM year = 2024 WHERE account ~ "Expenses" ORDER BY date DESC; -
Depuración Si una consulta no funciona como esperas, ejecuta primero una muestra pequeña con
LIMIT. Para probar un filtro, usaSELECT DISTINCTpara ver qué valores únicos coincide.-- Preview the first rows while iterating SELECT date, account, position LIMIT 5; -- Test which accounts match a regular expression SELECT DISTINCT account WHERE account ~ "^Assets:.*"; -
Aserciones de balance Puedes usar BQL para verificar las aserciones de
balanceen tu libro contable. Esta consulta debería devolver la cantidad exacta especificada en tu última verificación de balance para esa cuenta.-- Verify the final balance of your checking account SELECT account, sum(position) FROM close ON 2025-01-01 -- Use the date from your balance directive WHERE account = "Assets:Checking";