Xaprb

Stay curious!

GnuCash to MySQL export script

with 12 comments

Download gnucash2mysql and queries

It’s tax season, and this year I decided to export my GnuCash data into a MySQL database for analysis. This article is about that process, including source code for the export script and a couple of simple queries.

The script

GnuCash is a powerful double-entry accounting system for managing personal and/or business finances. I’ve used it since about 2002, and find it adequate for my needs. However, the reporting is widely acknowledged to be one of its weakest points. Reporting is implemented in the Scheme programming language, and if the included reports aren’t enough, most people probably won’t be able to write their own. Personally, I find it much easier (for certain reports) to write SQL queries instead. With that in mind, I set out to write a little script that will insert my GnuCash data into a MySQL database.

It’s actually a fairly simple task; the GnuCash developers chose XML as the file format, so the data is easily accessible from other programs. I decided to export only the account structure and transactions, leaving alone the business features such as customers and invoices.

Before I started, though, I spent some time investigating the built-in PostgreSQL backend. The default data format is XML, but the GnuCash developers also have support for storing the data in a PostgreSQL database. Unfortunately, I couldn’t get it to work. At least in version 1.8.11, it seems to be broken. In fact, the GnuCash website and developer mailing lists indicate this is a low priority for right now, as they are working towards a new version which uses an updated graphical toolkit (GTK+). Apparently the source is high-quality, but a bit out of date since the developers plan to revisit that once the new version is ready.

Satisfied that I’m not reinventing the wheel, I went ahead with a script to export the data myself. Since the XML format is really straightforward, I decided to do the simplest thing I could get to work. Perl seemed like a good choice. I am familiar with expat, and Perl has an expat module, so that was also a natural choice for the XML parsing. The basic idea is to push the data through the script like drinking from a firehose, and when it detects certain things — the start of an element, some character data, the end of an element — call a function to handle the data. Depending on what the data is (what element I’m currently examining, etc) I either store it for later reference, or push it into the database. I also automated creating the necessary table structure in the database.

Access to a MySQL database is required. The script uses tables called account, transaction and split. The DDL used to create the tables is at the end of the Perl script file. I used InnoDB tables so I’d have transaction support.

The script requires these Perl modules: DBI, Term::ProgressBar, and XML::Parser::Expat. You can run it with the --help option to see how to run it.

The queries

These queries assume precision decimal math. Versions of MySQL less than 5.0 use imprecise math. I wrote an article on MySQL and decimal math. If you have any issues with these queries, it may help to consult that article.

This query finds all unbalanced non-equity transactions by summing the splits:

select
    s.amount,
    a.name,
    t.description,
    t.posted
from account as a
    inner join (
        select transaction, sum(amount) as amount, max(account) as account
        from split
        group by transaction
        having sum(amount) <> 0
    ) as s on s.account = a.id
    inner join transaction as t on t.id = s.transaction
where a.type <> 'EQUITY'

This query sums all expenses for 2005 by month and account:

select date_format(posted, '%Y-%m') as month, name, sum(amount) as amount
from transaction as t
    inner join split as s on s.transaction = t.id
    inner join (
        select id, name from account
            where type='EXPENSE'
    ) as a on a.id = s.account
where year(posted) = 2005
group by date_format(posted, '%Y-%m'), name
order by date_format(posted, '%Y-%m'), name;

This query finds average monthly expenditures by account since January 2005:

select @num_months := count(distinct date_format(posted, '%Y-%m'))
    from transaction
    where posted >= '2005-01-01';

select cast(sum(amount) / @num_months as decimal(8,2)) as 'Average monthly amount',
    concat(coalesce(grandparent_name, ''),
        if(grandparent_name is null, '', ' > '),
        coalesce(parent_name, ''),
        if(parent_name is null, '', ' > '),
        name) as name
from (
    select date_format(posted, '%Y-%m') as month,
        a.name,
        aa.name as parent_name,
        aaa.name as grandparent_name,
        sum(amount) as amount
    from transaction as t
        inner join split as s on s.transaction = t.id
        inner join (
            select id, name, parent from account
            where type='EXPENSE'
        ) as a on a.id = s.account
        left outer join account as aa on aa.id = a.parent
        left outer join account as aaa on aaa.id = aa.parent
    where posted >= '2005-01-01'
    group by date_format(posted, '%Y-%m'), a.name
) as x
group by name
order by name;

Written by Xaprb

March 12th, 2006 at 1:14 pm

Posted in SQL, Tools, XML

12 Responses to 'GnuCash to MySQL export script'

Subscribe to comments with RSS or TrackBack to 'GnuCash to MySQL export script'.

  1. [...] GnuCash to MySQL export script – Xaprb Tagged as: accounting finance gnucash mysql scripting scripts software tools Tagged as: Links [...]

  2. Where is the script? The link above returns a 404!

    atongen

    12 Apr 07 at 12:43 am

  3. Sorry, that was an old link. The link at the very top of the article is correct.

    Xaprb

    13 Apr 07 at 8:34 am

  4. Version 0.1.8 of gnucash2mysql shortens all account and transaction id’s by one char since it creates id fields of 31 characters in length. Gnucash (I am using version 2.2.1) uses IDs of 32 characters in length.

    Here is my patch:

    — gnucash2mysql-orig 2007-08-27 16:59:26.000000000 0200
    gnucash2mysql 2008-01-31 10:33:15.000000000 0100
    @@ -416,10 416,10 @@
    drop table if exists account;

    create table account (
    - id char(31) not null primary key,
    id char(32) not null primary key,
    name varchar(255),
    type varchar(50),
    - parent char(31),
    parent char(32),
    description varchar(255),
    is_placeholder tinyint not null default 0,
    index(parent),
    @@ -430,17 430,17 @@
    ) type=InnoDB;

    create table transaction (
    - id char(31) not null primary key,
    id char(32) not null primary key,
    posted date not null,
    description varchar(255),
    index(posted)
    ) type=InnoDB;

    create table split (
    - id char(31) not null primary key,
    - transaction char(31) not null,
    id char(32) not null primary key,
    transaction char(32) not null,
    status char(1) not null default ”,
    - account char(31) not null,
    account char(32) not null,
    amount decimal(8,2) not null,
    memo varchar(255),
    foreign key FK_split_transaction(transaction)

    basman

    31 Jan 08 at 6:02 am

  5. Sweet, I was just about to code something similar myself when I found your script. Worked perfectly first time. I’ll get a lot of use out of this. Thanks so much for sharing it!

    Andrew

    26 Aug 08 at 4:54 pm

  6. I have been writing an OpenOffice.org macro that will read GnuCash output data and import it into OpenOffice.org’s embedded database, so that thereafter you can evaluate them e.g. in Ooo-Calc. I wrote this at first for myself only, but now I have made it more presentable and fit for general application.
    Downloading instructions and a detailed step-by-step description on its use can be found on my website
    http://www.alice-dsl.net/gnuc2ooo/gnuc2ooo_en/intro.html
    Kind regards
    Knut

    Knut

    22 Oct 08 at 1:01 am

  7. How to do I create the table account_category? When I run
    setup-categories.pl, I get “Undefined subroutine &main::prompt called at blib/lib/setup-categories.pl line 128.” I ran Makefile.PL and Makefile, then blib/lib/setup-categories.pl

    Steve Drach

    27 Oct 08 at 12:08 pm

  8. i may be an idiot, but what gnucash file does this program operate on? The gnucash data file that the program (gnucash) itself operates on appears to be binary. I don’t see any xml files anywhere. I can export my accounts as xml from gnucash, but that doesn’t contain transaction data. what am i missing?

    using gnucash 2.2.7

    thanks.

    rory

    4 Nov 08 at 12:47 am

  9. uh, nebbermind. just found out it’s a .gz file, even tho it doesn’t have a .gz extension.

    rory

    4 Nov 08 at 1:01 am

  10. how do you run the script? i’ve tried running it in a terminal but it tells me can’t open perl script no such file or directory.

    charles dandridge

    5 Dec 08 at 10:05 pm

  11. when i run it i get a No such file or directory at gnucash2mysql line 330, line 1.
    error

    charles dandridge

    5 Dec 08 at 11:25 pm

  12. cool, how would I go about modifying this to work with a postgres db?

    reuben

    26 Apr 09 at 12:56 pm

Leave a Reply