Beancount는 재무 데이터를 정밀하게 분류하고 분석할 수 있는 강력한 SQL 유사 쿼리 언어(BQL)를 제공합니다. 빠른 보고서를 생성하거나, 항목을 디버깅하거나, 복잡한 분석을 수행하려는 경우, BQL을 마스터하는 것이 평문 회계 장부의 잠재력을 최대한 활용하는 열쇠입니다. 이 가이드에서는 BQL의 구조, 함수, 모범 사례를 안내합니다. 🔍

쿼리 구조 및 실행
BQL의 핵심은 익숙한 SQL에서 영감을 받은 구문입니다. bea query로 쿼리를 실행하세요: bea --file <ledger> query "SELECT …"는 터미널에 테이블을 출력하고, 인수 없이 bea query를 실행하면 대화형 셸이 열립니다. 이 가이드의 모든 쿼리는 Beancount 3.2.3과 beanquery 0.2.0에서 실행되었습니다.
기본 쿼리 형식
BQL 쿼리는 SELECT, FROM, WHERE의 세 가지 주요 절로 구성됩니다.
SELECT <target1>, <target2>, ...
FROM <entry-filter-expression>
WHERE <posting-filter-expression>;SELECT: 검색하려는 데이터 열을 지정합니다.FROM: 처리되기 전에 전체 거래를 필터링합니다.WHERE: 거래가 선택된 후에 개별 전기 라인을 필터링합니다.
2단계 필터링 시스템
FROM과 WHERE 절의 차이를 이해하는 것은 정확한 쿼리를 작성하는 데 중요합니다. BQL은 2단계 필터링 프로세스를 사용합니다.
-
거래 수준 (
FROM) 이 절은 전체 거래에 작용합니다. 거래가FROM조건과 일치하면 전체 거래(모든 전기 포함)가 다음 단계로 전달됩니다. 이는 복식부기 시스템의 무결성을 유지하므로 데이터를 필터링하는 기본 방법입니다. 예를 들어,FROM year = 2024로 필터링하면 2024년에 발생한 모든 거래가 선택됩니다. -
전기 수준 (
WHERE) 이 절은FROM절에서 선택한 거래 내의 개별 전기를 필터링합니다. 이는 표시 및 거래의 특정 부분에 집중하는 데 유용합니다. 그러나 이 수준에서 필터링하면 항목의 한쪽만 볼 수 있으므로 출력에서 거래의 무결성이 "깨질" 수 있음을 유의하세요. 예를 들어,Expenses:Groceries계정에 대한 모든 전기를 선택할 수 있습니다.
구체적으로, PRINT FROM year = 2024는 전체 거래(각 항목의 양쪽 모두)를 반환하는 반면, SELECT date, narration, account, position FROM year = 2024 WHERE account ~ "Assets:Broker"는 일치하는 전기당 한 행을 반환합니다. 브로커 매수가 두 건인 장부에서 첫 번째는 전체 항목을 반환하고 두 번째는 정확히 두 개의 브로커 행을 반환합니다.
데이터 모델
데이터를 효과적으로 쿼리하려면 Beancount가 데이터를 구조화하는 방식을 이해해야 합니다. 장부는 지시문의 목록이지만, BQL은 주로 Transaction 항목에 초점을 맞춥니다.
트랜잭션 구조
각 Transaction은 최상위 속성과 Posting 객체 목록을 가진 컨테이너입니다.
Transaction
├── date
├── flag
├── payee
├── narration
├── tags
├── links
└── Postings[]
├── account
├── units
├── cost
├── price
└── metadata사용 가능한 열 유형
거래 또는 전기의 모든 속성을 SELECT할 수 있습니다.
-
거래 속성 이 열들은 단일 거래 내의 모든 전기에 대해 동일합니다.
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]) -
전기 속성 이 열들은 각 개별 전기 라인에 특정합니다.
SELECT account, -- 계정 이름 (str) position, -- 단위와 원가를 포함한 전체 금액 (Position) units(position), -- 전기의 수량과 통화 (Amount) cost(position), -- 전기의 원가 기준 (Amount) price, -- 전기에 사용된 가격 (Amount) weight, -- 원가 기준으로 환산된 포지션 (Amount) balance -- 계정 내 단위의 누적 합계 (Inventory)units와cost는position을 인수로 받는 함수입니다.price,weight,balance는 일반 열입니다.
쿼리 함수
BQL은 SQL처럼 집계 및 데이터 변환을 위한 함수 모음을 포함합니다.
집계 함수
집계 함수는 여러 행에 걸쳐 데이터를 요약합니다. GROUP BY와 함께 사용하면 그룹화된 요약을 제공합니다.
-- 전기 수 세기
SELECT COUNT(*)
-- 모든 전기를 하나의 Inventory로 합산; 통화와 로트는 변환되지 않고 유지됨
SELECT SUM(position)
-- 한 행, 예: (-2300.00 USD, 10 HOOL {150.00 USD, 2024-09-05}, 5 HOOL {160.00 USD, 2024-11-02})
-- 단일 통화로 한 계정 합계 구하기(가격이 없는 포지션은 통화를 유지)
SELECT SUM(CONVERT(position, 'USD')) WHERE account ~ "Assets:Checking"
-- 한 행, 예: (2580.00 USD)
-- 첫 번째와 마지막 거래의 날짜 찾기
SELECT FIRST(date), LAST(date)
-- 최소 및 최대 포지션 값 찾기
SELECT MIN(position), MAX(position)
-- 계정별로 그룹화하여 각 합계 구하기
SELECT account, SUM(position) GROUP BY account포지션/재고 함수
position 열은 복합 객체입니다. 이 함수들을 사용하면 특정 부분을 추출하거나 시장 가치를 계산할 수 있습니다.
-- 포지션에서 수량과 통화만 추출
SELECT UNITS(position)
-- 포지션의 총 원가 표시
SELECT COST(position)
-- 각 전기를 원가 가치로 표시 (열; WEIGHT() 함수는 없음)
SELECT account, weight WHERE account ~ "Assets:Investments"
-- 최신 가격 데이터를 사용하여 시장 가치 계산
-- (보유 자산에 대한 price 지시문 필요; 없으면 포지션이 그대로 반환됨)
SELECT VALUE(position)강력한 보고서를 위해 이들을 결합할 수 있습니다. 예를 들어, 투자 포트폴리오의 총 원가와 현재 시장 가치를 보려면 다음과 같이 합니다. 시장 가치를 위해서는 각 보유 자산에 대한 price 지시문이 필요합니다(예: 2024-12-01 price HOOL 175.00 USD). 두 집계 모두 계정별 Inventory를 반환하므로 각 통화는 여전히 별도로 나열됩니다.
SELECT
account,
COST(SUM(position)) AS total_cost,
VALUE(SUM(position)) AS market_value
FROM
account ~ "Assets:Investments"
GROUP BY
account
-- 계정당 한 행, 예: Assets:Broker:HOOL | (2300.00 USD) | (2625.00 USD)시장 가치 쿼리에 사용되는 가격 지시문은 수동 항목, 로컬 호가 수집기, 또는 호환 로더의 실시간 가격에서 올 수 있습니다. 관리형 피드는 쿼리 구문을 변경하지 않습니다. 로컬 업스트림 도구는 로컬 가격 파일이 필요하며, 과거 쿼리는 요청된 날짜 또는 그 이전의 가격이 여전히 필요합니다.
고급 기능
기본 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 파일 형식으로 출력하는 디버깅 도구입니다. 항목 필터만 허용합니다. 여기서 WHERE 절은 구문 오류입니다. 각 항목의 한쪽으로 출력을 좁히려면 대신 전기 필터와 함께 SELECT를 사용하세요. 일치하는 전기당 한 행을 반환합니다.
-- 모든 2024년 거래를 전체로 출력 (일치하는 각 항목의 모든 전기)
PRINT FROM year = 2024
-- 2024년 거래의 투자 전기만 표시
SELECT date, narration, account, position
FROM year = 2024
WHERE account ~ "Assets:Investments"
-- 고유 ID로 거래 찾기 (일부 도구에서 생성)
-- 일치하는 항목을 반환하거나, 해당 ID를 가진 항목이 없으면 행이 없음
PRINT FROM id = "8e7c47250d040ae2b85de580dd4f5c2a"필터링 표현식
논리 연산자(AND, OR), 정규식(~), 비교를 사용하여 정교한 필터를 작성할 수 있습니다.
문자열 리터럴은 작은따옴표를 사용합니다. 큰따옴표는 ~ 뒤의 정규식을 구분합니다.
-- 2024년 하반기의 모든 여행 경비 찾기
-- SELECT *는 일치하는 전기당 date, flag, payee, narration, position을 반환
SELECT * FROM
year = 2024 AND month >= 6
WHERE account ~ "Expenses:Travel"
-- 휴가 또는 출장과 관련된 모든 거래 찾기
SELECT * FROM
'vacation-2024' IN tags OR
'business-trip' IN links성능 고려 사항 ⚙️
bea query는 효율성을 위해 설계되었지만, 작동 흐름을 이해하면 대규모 장부에서 더 빠른 쿼리를 작성하는 데 도움이 됩니다.
- 데이터 로딩: Beancount는 먼저 전체 장부 파일을 파싱하고 모든 거래를 시간순으로 정렬합니다. 이 전체 데이터 세트는 메모리에 보관됩니다.
- 쿼리 최적화: 쿼리 엔진은 최대 효율을 위해 특정 순서로 필터를 적용합니다:
FROM(거래) ->WHERE(전기) -> 집계.FROM수준에서 필터링하는 것이 데이터 세트를 조기에 줄이므로 가장 빠릅니다. - 메모리 사용: 모든 작업은 메모리에서 발생합니다.
Position객체와Inventory집계는 최적화되어 있지만, 매우 큰 결과 세트는 상당한 RAM을 소비할 수 있습니다. BQL은 디스크 기반 임시 저장소를 사용하지 않습니다.
모범 사례
깔끔하고 효과적이며 유지 관리가 쉬운 쿼리를 작성하려면 다음 팁을 따르세요.
-
쿼리 구성 특히 복잡한 쿼리의 경우 가독성을 위해 형식을 지정하세요. 줄 바꿈과 들여쓰기로 절을 구분하세요.
-- 2024년 모든 경비를 위한 깔끔하고 읽기 쉬운 쿼리 SELECT date, account, position FROM year = 2024 WHERE account ~ "Expenses" ORDER BY date DESC; -
디버깅 쿼리가 예상대로 작동하지 않으면 먼저
LIMIT으로 작은 샘플을 실행하세요. 필터를 테스트하려면SELECT DISTINCT를 사용하여 일치하는 고유 값을 확인하세요.-- 반복하면서 처음 몇 행 미리보기 SELECT date, account, position LIMIT 5; -- 정규식과 일치하는 계정 테스트 SELECT DISTINCT account WHERE account ~ "^Assets:.*"; -
잔액 검증 BQL을 사용하여 장부의
balance검증을 재확인할 수 있습니다. 이 쿼리는 해당 계정에 대해 마지막 잔액 검사에 지정된 정확한 금액을 반환해야 합니다.-- 당좌 계정의 최종 잔액 확인 SELECT account, sum(position) FROM close ON 2025-01-01 -- 잔액 지시문의 날짜 사용 WHERE account = "Assets:Checking";