Перейти до основного вмісту

Мова запитів Beancount - SQL-подібні фінансові запити

Beancount має потужну, SQL-подібну мову запитів (BQL), яка дозволяє вам розрізати, розбивати та аналізувати ваші фінансові дані з точністю. Незалежно від того, чи хочете ви створити швидкий звіт, зневадити запис або виконати складний аналіз, опанування BQL є ключем до розкриття повного потенціалу вашої книги обліку у вигляді звичайного тексту. Цей посібник ознайомить вас з її структурою, функціями та найкращими практиками. 🔍


Структура та виконання запитів

Основою BQL є її знайомий, SQL-натхненний синтаксис. Запити виконуються за допомогою інструменту командного рядка bean-query, який обробляє ваш файл книги обліку та повертає результати безпосередньо у ваш термінал.

Базовий формат запиту

Запит BQL складається з трьох основних пунктів: SELECT, FROM та WHERE.

SELECT <ціль1>, <ціль2>, ...
FROM <вираз-фільтрації-записів>
WHERE <вираз-фільтрації-постингу>;
  • SELECT: Вказує, які стовпці даних ви хочете отримати.
  • FROM: Фільтрує цілі транзакції до їх обробки.
  • WHERE: Фільтрує окремі рядки постингу після вибору транзакції.

Дворівнева система фільтрації

Розуміння різниці між пунктами FROM та WHERE має вирішальне значення для написання точних запитів. BQL використовує дворівневий процес фільтрації.

  1. Рівень транзакції (FROM) Цей пункт впливає на цілі транзакції. Якщо транзакція відповідає умові FROM, вся транзакція (включно з усіма її постингами) передається на наступний етап. Це основний спосіб фільтрації даних, оскільки він зберігає цілісність системи бухгалтерського обліку подвійного запису. Наприклад, фільтрація FROM year = 2024 вибирає всі транзакції, які відбулися у 2024 році.

  2. Рівень постингу (WHERE) Цей пункт фільтрує окремі пости в межах транзакцій, вибраних пунктом FROM. Це корисно для представлення та для зосередження на конкретних частинах транзакції. Однак слід пам'ятати, що фільтрація на цьому рівні може "зламати" цілісність транзакції у вихідних даних, оскільки ви можете бачити лише одну сторону запису. Наприклад, ви можете вибрати всі пости до вашого рахунку Expenses:Groceries.


Модель даних

Щоб ефективно запитувати ваші дані, вам потрібно зрозуміти, як Beancount їх структурує. Книга обліку - це список директив, але BQL в основному зосереджується на записах Transaction.

Структура транзакції

Кожна Transaction є контейнером з атрибутами верхнього рівня та списком об'єктів Posting.

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

Доступні типи стовпців

Ви можете SELECT будь-який з атрибутів транзакції або її постингів.

  1. Атрибути транзакції Ці стовпці однакові для кожного постингу в межах однієї транзакції.

    SELECT
    date, -- Дата транзакції (datetime.date)
    year, -- Рік транзакції (int)
    month, -- Місяць транзакції (int)
    day, -- День транзакції (int)
    flag, -- Прапорець транзакції, наприклад, "*" або "!" (str)
    payee, -- Одержувач (str)
    narration, -- Опис або примітка (str)
    tags, -- Набір тегів, наприклад, #trip-2024 (set[str])
    links -- Набір посилань, наприклад, ^expense-report (set[str])
  2. Атрибути постингу Ці стовпці є специфічними для кожного окремого рядка постингу.

    SELECT
    account, -- Назва рахунку (str)
    position, -- Повна сума, включаючи одиниці та вартість (Position)
    units, -- Кількість та валюта постингу (Amount)
    cost, -- Базова вартість постингу (Cost)
    price, -- Ціна, використана в постингу (Amount)
    weight, -- Позиція, конвертована в її базову вартість (Amount)
    balance -- Поточний загальний обсяг одиниць на рахунку (Inventory)

Функції запитів

BQL включає набір функцій для агрегації та перетворення даних, як і SQL.

Функції агрегації

Функції агрегації підсумовують дані по кількох рядках. При використанні з GROUP BY вони надають згруповані підсумки.

-- Підрахувати кількість постингів
SELECT COUNT(*)

-- Підсумувати значення всіх постингів (конвертованих в спільну валюту)
SELECT SUM(position)

-- Знайти дату першої та останньої транзакції
SELECT FIRST(date), LAST(date)

-- Знайти мінімальне та максимальне значення позиції
SELECT MIN(position), MAX(position)

-- Згрупувати за рахунком, щоб отримати суму для кожного
SELECT account, SUM(position) GROUP BY account

Функції позиції/інвентаризації

Стовпець position є складеним об'єктом. Ці функції дозволяють витягувати конкретні частини з нього або обчислювати його ринкову вартість.

-- Витягнути лише число та валюту з позиції
SELECT UNITS(position)

-- Показати загальну вартість позиції
SELECT COST(position)

-- Показати позицію, конвертовану в її базову вартість (корисно для інвестицій)
SELECT WEIGHT(position)

-- Обчислити ринкову вартість, використовуючи останні дані про ціну
SELECT VALUE(position)

Ви можете поєднати їх для створення потужних звітів. Наприклад, щоб побачити загальну вартість та поточну ринкову вартість вашого інвестиційного портфеля:

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

Розширені функції

Окрім базових операторів SELECT, BQL пропонує спеціалізовані команди для типових фінансових звітів.

Звіти про залишки

Оператор BALANCES генерує баланс або звіт про прибутки та збитки за певний період.

-- Створити простий баланс станом на початок 2024 року
BALANCES FROM close ON 2024-01-01
WHERE account ~ "^Assets|^Liabilities"

-- Створити звіт про прибутки та збитки за 2024 фінансовий рік
BALANCES FROM
OPEN ON 2024-01-01
CLOSE ON 2024-12-31
WHERE account ~ "^Income|^Expenses"

Журнальні звіти

Оператор JOURNAL показує детальну активність для одного або кількох рахунків, подібно до традиційного перегляду книги обліку.

-- Показати всю активність на вашому розрахунковому рахунку за його початковою вартістю
JOURNAL "Assets:Checking" AT COST

-- Показати всі транзакції 401k, відображаючи лише одиниці (акції)
JOURNAL "Assets:.*:401k" AT UNITS

Операції друку

Оператор PRINT - це інструмент налагодження, який виводить повні відповідні транзакції в їх оригінальному форматі файлу Beancount.

-- Друкувати всі транзакції, пов'язані з інвестиціями, з 2024 року
PRINT FROM year = 2024
WHERE account ~ "Assets:Investments"

-- Знайти транзакцію за її унікальним ідентифікатором (створеним деякими інструментами)
PRINT FROM id = "8e7c47250d040ae2b85de580dd4f5c2a"

Вирази фільтрації

Ви можете створювати складні фільтри за допомогою логічних операторів (AND, OR), регулярних виразів (~) та порівнянь.

-- Знайти всі витрати на проїзд за другу половину 2024 року
SELECT * FROM
year = 2024 AND month >= 6
WHERE account ~ "Expenses:Travel"

-- Знайти всі транзакції, пов'язані з відпусткою або бізнесом
SELECT * FROM
"vacation-2024" IN tags OR
"business-trip" IN links

Міркування щодо продуктивності ⚙️

bean-query розроблено для ефективності, але розуміння його операційного потоку може допомогти вам писати швидші запити до великих книг обліку.

  1. Завантаження даних: Beancount спочатку розбирає весь ваш файл книги обліку та сортує всі транзакції в хронологічному порядку. Весь цей набір даних зберігається в пам'яті.
  2. Оптимізація запитів: Механізм запитів застосовує фільтри в певному порядку для максимальної ефективності: FROM (транзакції) -> WHERE (пости) -> Агрегації. Фільтрація на рівні FROM є найшвидшою, оскільки вона зменшує набір даних на ранньому етапі.
  3. Використання пам'яті: Усі операції відбуваються в пам'яті. Об'єкти Position та агрегації Inventory оптимізовані, але дуже великі набори результатів можуть споживати значний обсяг оперативної пам'яті. BQL не використовує тимчасове сховище на диску.

Найкращі практики

Дотримуйтесь цих порад, щоб писати чисті, ефективні та зручні для підтримки запити.

  1. Організація запитів Форматуйте свої запити для зручності читання, особливо складні. Використовуйте розриви рядків та відступи для розділення пунктів.

    -- Чистий, зручний для читання запит для всіх витрат 2024 року
    SELECT
    date,
    account,
    position
    FROM
    year = 2024
    WHERE
    account ~ "Expenses"
    ORDER BY
    date DESC;
  2. Налагодження Якщо запит не працює належним чином, використовуйте EXPLAIN, щоб побачити, як Beancount його розбирає. Щоб перевірити фільтр, використовуйте SELECT DISTINCT, щоб побачити, яким унікальним значенням він відповідає.

    -- Побачити план запиту
    EXPLAIN SELECT date, account, position;

    -- Перевірити, які рахунки відповідають регулярному виразу
    SELECT DISTINCT account
    WHERE account ~ "^Assets:.*";
  3. Твердження про залишок Ви можете використовувати BQL, щоб перевірити твердження про balance у вашій книзі обліку. Цей запит повинен повернути точну суму, вказану в вашій останній перевірці балансу для цього рахунку.

    -- Перевірити кінцевий баланс вашого розрахункового рахунку
    SELECT account, sum(position)
    FROM close ON 2025-01-01 -- Використовуйте дату з вашої директиви балансу
    WHERE account = "Assets:Checking";