Instructions to use a import GnuCash data into a spreadsheet with a PostgreSQL backend
Mark Sluser
marksluser at mac.com
Mon Jul 20 03:40:54 EDT 2009
There were a couple of errors in my last posting of trigger function
"build_acct_tree"
1. "accounts_tree" was not able to be deleted/dropped because it was
in use by view "transactions_tree". This view is now deleted at the
start of the function and then dynamically added after it is updated.
2. The lev_name0 column was always blank because a root GUID does not
have a name. This column is now updated to 'ROOT0'
Also there was a problem with the view "transactions_tree"
1. View "transactions_tree" displayed the entered date rather than the
posted date.
Here is the updated "build_acct_tree" code:
DECLARE
d integer := 0;
r integer := 0;
txt_lev_guid TEXT := 'lev_guid';
txt_lev_name TEXT := 'lev';
txt_exec TEXT;
rs RECORD;
BEGIN
DROP VIEW IF EXISTS transactions_tree;
DROP TABLE IF EXISTS accounts_tree;
CREATE TABLE accounts_tree(guid character varying, parent_guid
character varying,
name character varying, depth integer, lineage character varying[]);
INSERT INTO accounts_tree (guid, parent_guid, name)
SELECT guid, parent_guid, name FROM accounts;
FOR rs IN
SELECT DISTINCT accounts.parent_guid
FROM accounts LEFT JOIN accounts A2
ON accounts.parent_guid = A2.guid
WHERE A2.guid IS NULL
LOOP
UPDATE accounts_tree
SET lineage=array_append('{}',rs.parent_guid), depth=0
FROM accounts
WHERE accounts_tree.parent_guid = rs.parent_guid;
END LOOP;
EXECUTE
'ALTER TABLE accounts_tree
ADD COLUMN '|| txt_lev_guid || d || ' character varying ,
ADD COLUMN '|| txt_lev_name || d || ' character varying';
EXECUTE '
UPDATE accounts_tree
SET ' || txt_lev_guid || d || ' = lineage['|| d ||'+1]
WHERE accounts_tree.depth >= ' || d ;
EXECUTE '
UPDATE accounts_tree
SET '|| txt_lev_name || d || ' = accounts.name
FROM accounts
WHERE accounts_tree.'|| txt_lev_guid || d || ' = accounts.guid';
WHILE (EXISTS (SELECT * FROM accounts_tree WHERE depth Is Null)) LOOP
d := d + 1;
UPDATE accounts_tree
SET depth = A2.depth + 1,
lineage = array_append(A2.lineage, accounts_tree.parent_guid)
FROM accounts_tree as A2
WHERE accounts_tree.parent_guid=A2.guid
AND A2.depth>=0
AND A2.lineage Is Not Null
AND accounts_tree.depth Is Null;
EXECUTE
'ALTER TABLE accounts_tree
ADD COLUMN '|| txt_lev_guid || d || ' character varying ,
ADD COLUMN '|| txt_lev_name || d || ' character varying';
txt_exec := 'UPDATE accounts_tree SET ';
txt_exec := txt_exec || txt_lev_guid || '0 = lineage[1] ';
FOR i IN 1..d LOOP
txt_exec := txt_exec || ',' || txt_lev_guid|| i || ' = lineage['|| i
||'+1] ';
END LOOP;
txt_exec := txt_exec || 'WHERE accounts_tree.depth >= ' || d ;
EXECUTE txt_exec;
END LOOP;
FOR rs IN
SELECT DISTINCT accounts_tree.lev_guid0
FROM accounts_tree
LOOP
UPDATE accounts_tree
SET lev0='ROOT' || r
WHERE accounts_tree.lev_guid0 = rs.lev_guid0;
r := r + 1;
END LOOP;
FOR i IN 1..d LOOP
EXECUTE '
UPDATE accounts_tree
SET '|| txt_lev_name || i || ' = accounts.name
FROM accounts
WHERE accounts_tree.'|| txt_lev_guid || i || ' = accounts.guid';
END LOOP;
txt_exec := 'CREATE VIEW transactions_tree AS ';
txt_exec := txt_exec || 'SELECT
accounts.account_type,';
FOR i IN 0..d LOOP
txt_exec := txt_exec || '
accounts_tree.'|| txt_lev_name || i || ',';
END LOOP;
txt_exec := txt_exec || '
accounts."name",
CAST(substring(transactions.post_date FROM 1 FOR 4) || ''-'' ||
substring(transactions.post_date FROM 5 FOR 2) || ''-'' ||
substring(transactions.post_date FROM 7 FOR 2) AS date) AS post_date,
CAST(substring(transactions.post_date FROM 1 FOR 4) AS integer) AS
yyyy,
CAST(substring(transactions.post_date FROM 5 FOR 2) AS integer) AS
mm,
CAST(substring(transactions.post_date FROM 7 FOR 2) AS integer) AS
dd,
transactions.num,
transactions.description,
splits.memo,
CAST(splits.quantity_num AS decimal)/splits.quantity_denom AS
quantity_num,
CAST(splits.value_num AS decimal)/splits.value_denom AS value_num
FROM
public.accounts,
public.accounts_tree,
public.splits,
public.transactions
WHERE
splits.account_guid = accounts.guid AND
splits.account_guid = accounts_tree.guid AND
splits.tx_guid = transactions.guid';
EXECUTE txt_exec;
RETURN NEW;
END;
Enjoy,
-Mark
On 16-Jul-09, at 12:14 AM, Mark Sluser wrote:
> If you use PostgreSQL to store your data instead of the usual XML
> file, you can use a spreadsheet to filter your data in a way that
> the GnuCash reporting engine won't let you.
>
> This requires
> 1. Preparing a PostgreSQL backend
> 2. Modifying the gnucash database
> 3. Importing the data into a spreadsheet
>
> 1. Prepare a PostgreSQL database backend
> --------------------------------------------------------
>
> 1. Download the latest version of GnuCash that supports a database
> backend (as of today this would be 2.3.2 unstable) and install it.
> 2. Download PostgreSQL from www.postgresql.org. You will need at
> least 8.4 (that is the latest stable release to date) and install it.
> 3. Configure PostgreSQL with all the defaults. The documentation is
> pretty thorough.
> 4. Open the PGAdmin3 database administration tool and create a new
> database called 'gnucash' under the postgres username. Use a UTF-8
> text encoding.
> 5. Start GnuCash and open your existing XML file.
> 6. Save your data to the database by going File->Save As
> 7. Select postgresql from the destination list and enter the
> settings you used when setting up postgresql (such as username and
> password). Here is what I used:
> host: localhost
> database: gnucash
> username: postgres
> password: {your password here}
> 8. Wait for the database to be saved. Now it is available for
> viewing in the PGAdmin3 database administration tool.
>
> Most of the above has been mentioned before in other newsgroup
> discussions. Now the interesting part.
> Initially I used the OpenOffice extension 'gnuc2ooo' to import my
> data into openoffice base. The OO extension can be found here http://www.alice-dsl.net/gnuc2ooo/gnuc2ooo_en/intro.html
> .
> This macro provided access to my data in a nice table of
> transactions and I could filter it using the usual spreadsheet
> tools. However every time I made a change to my data, I had to re-
> run the gnuc2ooo import macro, and it was pretty slow for my data
> (my XML file is 14.5 MB). Once gnucash released their database
> backend version I figured I could build a nice table of transactions
> using a SQL query. This would give me access to any changed in my
> data immediately and make storing my 14.5MB of data way more
> efficient.
>
> The format of the transaction table provided in gnuc2ooo made it
> easy to filter gnucash's account tree heirarchy because it created a
> column for each level in the tree.
> This meant that
> LEVEL1 had the accounts Assets, Income, Equity, Expenses, and
> Liabilities.
> LEVEL2 had sub accounts such as 'Accounts Payable', 'Accounts
> Receivables', Sales, Groceries, Property Taxes, Visa, Utilities, etc.
> LEVEL3 had sub accounts such as Gas, Electricity, Cable, Water, etc.
>
> So If I wanted to see the transactions were for utilities, I would
> filter LEVEL1=Expenses and LEVEL2=Utilities. If I wanted to see what
> transactions I had for Electricity, I would add LEVEL3=Electricity.
> And if you sum the amount column, you can easily get totals for
> reports.
> This makes the data really simple and easy to work with.
>
> 2. Modifying the gnucash database
> -----------------------------------------------
>
> To get the same Level1, Level2, etc. type columns in PostgreSQL you
> need to create 3 things in the gnucash database.
> 1. A trigger function -- plpgSQL script to create a new accounts
> table called accounts_tree which will contain the extra information
> required to produce a nice table view.
> 2. A trigger -- tells PostgreSQL when to execute the Trigger
> function. It must be associated with a table.
> 3. A View -- this will be the table view that the spreadsheet can
> connect to.
>
>
> 2.1. Add a Trigger Function
> -----------------------------------
> Trigger functions are usually written in PostgreSQL's procedural
> language called plpgSQL and this one is too.
> Open the PgAdmin3 tool and expand the tree to gnucash->schemas-
> >public->Trigger Functions
> Right click on Trigger Functions and select "New Trigger Function ..."
> Set the following properties:
> name: build_acct_tree
> owner: postgres
> language: plpgsql
>
> Select the definition tab and past the following code:
>
>
> DECLARE
> d integer := 0;
> txt_lev_guid TEXT := 'lev_guid';
> txt_lev_name TEXT := 'lev_name';
> txt_exec TEXT;
> rs RECORD;
> BEGIN
>
> DROP TABLE IF EXISTS accounts_tree;
>
> CREATE TABLE accounts_tree(guid character varying, parent_guid
> character varying,
> name character varying, depth integer, lineage character varying[]);
>
> INSERT INTO accounts_tree (guid, parent_guid, name)
> SELECT guid, parent_guid, name FROM accounts;
>
> FOR rs IN
> SELECT DISTINCT accounts.parent_guid
> FROM accounts LEFT JOIN accounts A2
> ON accounts.parent_guid = A2.guid
> WHERE A2.guid IS NULL
> LOOP
>
> UPDATE accounts_tree
> SET lineage=array_append('{}',rs.parent_guid), depth=0
> FROM accounts
> WHERE accounts_tree.parent_guid = rs.parent_guid;
>
> END LOOP;
>
>
> EXECUTE
> 'ALTER TABLE accounts_tree
> ADD COLUMN '|| txt_lev_guid || d || ' character varying ,
> ADD COLUMN '|| txt_lev_name || d || ' character varying';
>
> EXECUTE '
> UPDATE accounts_tree
> SET ' || txt_lev_guid || d || ' = lineage['|| d ||'+1]
> WHERE accounts_tree.depth >= ' || d ;
>
> EXECUTE '
> UPDATE accounts_tree
> SET '|| txt_lev_name || d || ' = accounts.name
> FROM accounts
> WHERE accounts_tree.'|| txt_lev_guid || d || ' = accounts.guid';
>
> WHILE (EXISTS (SELECT * FROM accounts_tree WHERE depth Is Null)) LOOP
>
> d := d + 1;
>
> UPDATE accounts_tree
> SET depth = A2.depth + 1,
> lineage = array_append(A2.lineage, accounts_tree.parent_guid)
> FROM accounts_tree as A2
> WHERE accounts_tree.parent_guid=A2.guid
> AND A2.depth>=0
> AND A2.lineage Is Not Null
> AND accounts_tree.depth Is Null;
>
>
> EXECUTE
> 'ALTER TABLE accounts_tree
> ADD COLUMN '|| txt_lev_guid || d || ' character varying ,
> ADD COLUMN '|| txt_lev_name || d || ' character varying';
>
> txt_exec := 'UPDATE accounts_tree SET ';
> txt_exec := txt_exec || txt_lev_guid || '0 = lineage[1] ';
>
> FOR i IN 1..d LOOP
> txt_exec := txt_exec || ',' || txt_lev_guid|| i || ' = lineage['||
> i ||'+1] ';
> END LOOP;
>
> txt_exec := txt_exec || 'WHERE accounts_tree.depth >= ' || d ;
>
> EXECUTE txt_exec;
>
> END LOOP;
>
> FOR i IN 0..d LOOP
> EXECUTE '
> UPDATE accounts_tree
> SET '|| txt_lev_name || i || ' = accounts.name
> FROM accounts
> WHERE accounts_tree.'|| txt_lev_guid || i || ' = accounts.guid';
> END LOOP;
>
>
> RETURN NEW;
>
> END;
>
>
> Select OK and you should see the new trigger function appear in the
> tree view.
>
> 2.2. Add a PostgreSQL Trigger
> ---------------------------------------
>
> Navigate the tree view to gnucash->schemas->public->Tables->accounts-
> >Triggers
> Right click on Triggers and select "New Trigger"
>
> Set the following properties:
> name: build_tree
> trigger function: build_acct_tree
> Fires: AFTER
> Events: INSERT, UPDATE, DELETE (all selected)
>
> Click OK and you should see the new trigger appear in the tree view.
>
>
> 2.3. Add a PostgreSQL View
> ------------------------------------
> Navigate the tree view to gnucash->schemas->public->Tables->Views
> Right click on Views and select "New View..."
>
> Set the following properties:
> name: transactions_tree
> owner: postgres
>
> Select the definition tab and past the following code:
>
> SELECT
> accounts.account_type,
> accounts_tree.lev_name0,
> accounts_tree.lev_name1,
> accounts_tree.lev_name2,
> accounts_tree.lev_name3,
> accounts_tree.lev_name4,
> accounts_tree.lev_name5,
> accounts_tree.lev_name6,
> accounts."name",
> substring(transactions.enter_date FROM 1 FOR 4) AS integer) || '-'
> substring(transactions.enter_date FROM 5 FOR 2) AS integer) || '-'
> substring(transactions.enter_date FROM 7 FOR 2) AS integer) AS
> 'date',
>
> CAST(substring(transactions.enter_date FROM 1 FOR 4) AS integer) AS
> yyyy,
> CAST(substring(transactions.enter_date FROM 5 FOR 2) AS integer) AS
> mm,
> CAST(substring(transactions.enter_date FROM 7 FOR 2) AS integer) AS
> dd,
> transactions.num,
> transactions.description,
> splits.memo,
> CAST(splits.quantity_num AS decimal)/splits.quantity_denom AS
> quantity_num,
> CAST(splits.value_num AS decimal)/splits.value_denom AS value_num
> FROM
> public.accounts,
> public.accounts_tree,
> public.splits,
> public.transactions
> WHERE
> splits.account_guid = accounts.guid AND
> splits.account_guid = accounts_tree.guid AND
> splits.tx_guid = transactions.guid;
>
>
> Click OK and you should see the new view appear in the tree view.
> Now the database is ready to be queried and viewed in your
> spreadsheet.
>
> 3. View the data in a spreadsheet
> ------------------------------------------------------------------
> I am writing this for windows people using Microsoft Excel.
>
> 3.1. Create a data connection
> -------------------------------------
> Because I am writing this for windows the data connection will have
> to be an ODBC data connection. When you installed PostgreSQL on your
> computer you may not have also installed the ODBC driver for windows.
> To install the driver start the PostgreSQL 'Application Stack
> Builder' which should be available from Start->Programs->PostgreSQL
> 8.4->Application Stack Builder.
> Hopefully you can manage installing the driver once you see the
> stack builder.
>
> To setup the ODBC data connection open the ODBC configuration tool
> located at Start->Control Panel->Administrative Tools->Data
> Sources(ODBC)
> Under the User tab, click Add.
> Scroll to the bottom of the list and select 'PostgreSQL Unicode' and
> click Finish
> Set the following properties:
> datasource: PostgreSQL_GnuCash
> description: PostgreSQL to GnuCash
> database: gnucash
> SSL Mode: disable
> server: localhost
> port: 5432
> username: postgres
> password: {your password here}
>
> Click the test button to ensure the connection works.
> If the test fails, check username, password and the port. Port 5432
> is the default.
> Note: if your database exists on another server, you will have to
> edit the pg_hba.conf file on the server to allow remote connections.
>
> Click Save and then Ok to close the ODBC configuration tool.
>
>
> 3.2. Insert the data into a spreadsheet
> -------------------------------------------------
> Most spreadsheets programs have detailed instructions on how to
> connect to a database in their help systems.
> I have Office 2007 for windows so here is what I did:
>
> Click the Data Tab
> Click 'From External Sources'
> Click 'From Data Connection Wizard'
> Select 'ODBC DSN' and click Next
> Select 'PostgreSQL_GnuCash' and click Next
> Select 'transactions_tree' and click Next
> Click Finish
>
> Import Data window appears
> Click OK
> The transactions data will fill your worksheet
>
> Now you can use column filters and a totals row(Excel 2007 feature)
> to get totals of transactions that were not possible with the
> GnuCash reporting engine.
>
> Enjoy.
>
>
>
> Mark Sluser
> Calgary, AB
>
>
More information about the gnucash-user
mailing list