Beancount dispone di un potente Query Language (BQL) simile a SQL che ti permette di suddividere, analizzare e studiare i tuoi dati finanziari con precisione. Che tu voglia generare un report rapido, fare il debug di una scrittura o eseguire analisi complesse, padroneggiare BQL è la chiave per sbloccare il pieno potenziale del tuo registro contabile in plaintext. Questa guida ti accompagnerà attraverso la sua struttura, le funzioni e le best practice. 🔍

Struttura ed Esecuzione delle Query
Il cuore di BQL è la sua familiare sintassi ispirata a SQL. Esegui le query con bea query: bea --file <ledger> query "SELECT …" stampa la tabella nel tuo terminale, e bea query senza argomenti apre la shell interattiva. Ogni query in questa guida è stata eseguita con Beancount 3.2.3 con beanquery 0.2.0.
Struttura Base di una Query
Una query BQL è composta da tre clausole principali: SELECT, FROM e WHERE.
SELECT <target1>, <target2>, ...
FROM <entry-filter-expression>
WHERE <posting-filter-expression>;SELECT: Specifica quali colonne di dati vuoi recuperare.FROM: Filtra intere transazioni prima che vengano elaborate.WHERE: Filtra le singole righe di posting dopo che la transazione è stata selezionata.
Sistema di Filtraggio su Due Livelli
Comprendere la differenza tra le clausole FROM e WHERE è cruciale per scrivere query accurate. BQL utilizza un processo di filtraggio a due livelli.
-
Livello transazione (
FROM) Questa clausola agisce su intere transazioni. Se una transazione corrisponde alla condizioneFROM, l'intera transazione (incluse tutte le sue scritture) viene passata alla fase successiva. Questo è il modo principale per filtrare i dati, poiché preserva l'integrità del sistema di partita doppia. Ad esempio, filtrandoFROM year = 2024si selezionano tutte le transazioni avvenute nel 2024. -
Livello posting (
WHERE) Questa clausola filtra le singole scritture all'interno delle transazioni selezionate dalla clausolaFROM. Questo è utile per la presentazione e per concentrarsi su specifiche parti di una transazione. Tuttavia, tieni presente che filtrare a questo livello può "rompere" l'integrità di una transazione nell'output, poiché potresti vedere solo un lato di una scrittura. Ad esempio, potresti selezionare tutte le scritture sul tuo contoExpenses:Groceries.
Concretamente, PRINT FROM year = 2024 restituisce le transazioni complete (entrambi i lati di ogni scrittura), mentre SELECT date, narration, account, position FROM year = 2024 WHERE account ~ "Assets:Broker" restituisce una riga per ogni posting corrispondente. Su un registro con due acquisti tramite broker, la prima restituisce le scritture complete e la seconda restituisce esattamente le due righe del broker.
Modello dei Dati
Per interrogare i tuoi dati in modo efficace, devi capire come Beancount li struttura. Un registro è una lista di direttive, ma BQL si concentra principalmente sulle scritture Transaction.
Struttura della Transazione
Ogni Transaction è un contenitore con attributi di primo livello e una lista di oggetti Posting.
Transaction
├── date
├── flag
├── payee
├── narration
├── tags
├── links
└── Postings[]
├── account
├── units
├── cost
├── price
└── metadataTipi di Colonne Disponibili
Puoi usare SELECT su qualsiasi attributo della transazione o delle sue scritture.
-
Attributi della transazione Queste colonne sono identiche per ogni posting all'interno di una singola transazione.
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]) -
Attributi del posting Queste colonne sono specifiche di ogni singola riga di scrittura.
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)unitsecostsono funzioni che accettanoposition.price,weightebalancesono colonne semplici.
Funzioni di Query
BQL include una suite di funzioni per l'aggregazione e la trasformazione dei dati, proprio come SQL.
Funzioni di Aggregazione
Le funzioni di aggregazione riassumono i dati su più righe. Se utilizzate con GROUP BY, forniscono riepiloghi raggruppati.
-- 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 accountFunzioni di Posizione/Inventario
La colonna position è un oggetto composito. Queste funzioni ti permettono di estrarne parti specifiche o di calcolarne il valore di mercato.
-- 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)Puoi combinarle per creare report potenti. Ad esempio, per vedere il costo totale e il valore di mercato corrente del tuo portafoglio di investimenti. Il valore di mercato necessita di una direttiva price per ogni holding (ad esempio 2024-12-01 price HOOL 175.00 USD). Entrambe le aggregazioni restituiscono un Inventory per conto, quindi ogni valuta è ancora elencata separatamente.
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)Le direttive price utilizzate dalle query sul valore di mercato possono provenire da voci manuali, da un fetcher locale di quotazioni o da Live Prices in un loader compatibile. I feed gestiti non cambiano la sintassi delle query. Gli strumenti upstream locali necessitano di file di prezzi locali, e le query storiche richiedono comunque prezzi alla data richiesta o precedenti.
Funzionalità Avanzate
Oltre alle istruzioni SELECT di base, BQL offre comandi specializzati per i report finanziari più comuni.
Report di Bilancio
L'istruzione BALANCES genera uno stato patrimoniale o un conto economico per un periodo specifico.
-- 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"Report di Giornale
L'istruzione JOURNAL mostra l'attività dettagliata di uno o più conti, in modo simile a una tradizionale vista di registro.
-- 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 UNITSOperazioni di Stampa
L'istruzione PRINT è uno strumento di debug che produce transazioni complete e corrispondenti nel loro formato originale di file Beancount. Accetta solo un filtro sulle scritture. Una clausola WHERE qui è un errore di sintassi. Per restringere l'output a un lato di ogni scrittura, usa SELECT con un filtro sui posting. Restituisce una riga per ogni posting corrispondente.
-- 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"Espressioni di Filtraggio
Puoi costruire filtri sofisticati usando operatori logici (AND, OR), espressioni regolari (~) e confronti.
I letterali stringa usano virgolette singole. Le virgolette doppie delimitano l'espressione regolare dopo ~.
-- 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 linksConsiderazioni sulle Prestazioni
bea query è progettato per l'efficienza, ma comprendere il suo flusso operativo può aiutarti a scrivere query più veloci su registri di grandi dimensioni.
- Caricamento dei dati: Beancount prima analizza l'intero file del tuo registro e ordina tutte le transazioni cronologicamente. L'intero dataset è mantenuto in memoria.
- Ottimizzazione della query: Il motore di query applica i filtri in un ordine specifico per la massima efficienza:
FROM(transazioni) ->WHERE(postings) -> Aggregazioni. Filtrare a livelloFROMè più veloce perché riduce il dataset in anticipo. - Utilizzo della memoria: Tutte le operazioni avvengono in memoria. Gli oggetti
Positione le aggregazioniInventorysono ottimizzati, ma set di risultati molto grandi possono consumare RAM significativa. BQL non utilizza archiviazione temporanea su disco.
Migliori Pratiche
Segui questi consigli per scrivere query pulite, efficaci e manutenibili.
-
Organizzazione delle query Formatta le tue query per la leggibilità, specialmente quelle complesse. Usa interruzioni di riga e indentazione per separare le clausole.
-- A clean, readable query for all 2024 expenses SELECT date, account, position FROM year = 2024 WHERE account ~ "Expenses" ORDER BY date DESC; -
Debugging Se una query non funziona come previsto, esegui prima un piccolo campione con
LIMIT. Per testare un filtro, usaSELECT DISTINCTper vedere quali valori unici corrisponde.-- 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:.*"; -
Assertion di saldo Puoi usare BQL per ricontrollare le assertion di
balancenel tuo registro. Questa query dovrebbe restituire l'importo esatto specificato nel tuo ultimo controllo di saldo per quel conto.-- 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";