Matthew
Matthew

Reputation: 7725

Storing 'debits' and 'credits' to maintain a 'transactions' table

Which is the better schema for a transactions table:

customer_id
type (enum, 'Debit' or 'Credit')
amount (decimal, unsigned)

or

customer_id
amount (decimal, signed... negative numbers are debits, positive numbers are credits)

The second solution seems simpler, but I'm worried I'm missing some potential pitfalls.

Upvotes: 8

Views: 8000

Answers (11)

Metryg
Metryg

Reputation: 1

Late off the mark, but anyway: Two cents. I (accountant and keen DIY programmer) am surprised at some of the answers here. The nomenclature of accounting is irrelevant at machine level. Polarity of amounts can be dealt with using +/- in contextual tables (say sales vs purchases), or a common table with an 8-bit flag variable denoting context, instead of using naive ‘debit and ‘credit’ columns. At the instant of capture it does not make any/much difference in terms of machine speed/capability. When it comes to drawing reports (daily/monthly…) there could well be a marked difference between reading two decimals off disk vs in-memory bit masking to classify an amount. Code readability? Up to the coder. Perhaps more importantly, whether to use decimal, or adjusting integer at GUI-level, but that’s another topic...

Upvotes: 0

In fact, the question was asked quite a while ago, but in search of my answer in 2022, this article came across to me first on Google, so I will write my solution here. I would just put it in the type table

transaction_type
id name operationType(true|false)

transaction
id amount type_id ...

And about the fact that balances may not coincide with transactions. I don't know how it was in 2009, but now we can use internal transactions in the database to make sure that all queries are completed, otherwise roll everything back, and also freeze the balance field of the user. This ensures that parallel queries cannot be executed. They will wait until the transaction is completed and the balance field is unfrozen

Upvotes: 0

stevegt
stevegt

Reputation: 1742

Background

Debits represent things you own, and credits represent things others own. They aren't the same dimensional units, and shouldn't be stored in the same database column. Using the sign bit to represent debits or credits is an oversimplification of how double-entry bookkeeping works; this oversimplification keeps popping up in low-end and homebrew accounting packages though, probably because it's the way laypeople think about accounting. [1]

The easiest way I've found to bring software devs up to speed with double-entry bookkeeping is to note that a number in an accounting system isn't a scalar -- it's a vector. The vector elements consist of a dimensional axis (debit or credit) as well as a magnitude (a signed fixed-place decimal). [2]

Solutions

Your first solution represents the vector nature of the data, and follows generally accepted accounting practices, but still stores the magnitude element in the same column, regardless of which axis it applies to. This makes SELECT statements more complicated.

It would be better to split the debit and credit magnitudes into separate columns; this gets rid of the need for the axis (enum) column, simplifies the SQL, is probably a performance improvement, and is the more conventional approach.

Your second solution (overloading the sign bit to represent debits or credits) scares me every time I see it, because I can never be sure if the architect is somehow compensating elsewhere for the lost dimensional information, or just didn't understand the vector nature of accounting data. Judging by what I see on SO, there are apparently a lot of accounting packages written that way, but it makes for complex, fragile, slower code and data structures, all in the interests of saving a tiny fraction of database space.

Source

Once upon a time, I was a trading systems engineer for an international bank. Corner cases bad, simple code good.

Footnotes

[1]: I think folks stumble into thinking "negative values are debits" in part because of the way banking works; banks use language which gives people the wrong impression of what a debit is. From the perspective of the bank, your checking account is something someone else owns -- it has a credit balance. When you deposit money in the bank, they tell you they are "crediting" your account, and when you withdraw, they say "debiting". But that's all from the bank's perspective, so it's all backwards. In your own accounting system (if you had one), debiting your checking account means increasing the balance, and a credit is a decrease. From your perspective, that account has a debit balance, because it's something you own.

[2]: If your language supports fixed-decimal-place complex numbers, they might be a handy way to manipulate accounting data; debits might be on the real axis and credits might be imaginary. This creates some interesting properties; a balanced set of entries would have a phase angle of 45 degrees, and so on. But unless your DB engine supports complex numbers natively, you'd wind up splitting the real and complex components into two columns for storage anyway, and those columns would be called "debits" and "credits".

Upvotes: 9

Thorsten Deinert
Thorsten Deinert

Reputation: 31

The second schema doesn't support basic accounting principles, as stated. The first schema would be one way to store the data, the other that makes sense would be to have two seperate columns for credit and debit.

What I really would liked to comment is the answer from Doug McClean. You can have only debit and credit one account, that would enforce a correct balance for sure. However, in reality, there is often more than one account involved, e.g. for taxes. So that model doesn't capture accounting well.

Upvotes: 3

user2673781
user2673781

Reputation: 1

I recommend that Debits and credits may have to remain as separate columns with 'unsigned' attribute.

Resultant calculated Balance column (i.e. cumulative debits summation- cumulative credits summation columns) will have to show a negative value with minus sign(-) when debits exceed credits.

When it comes to working out a query to get trial balance on a given date, this should be zero as per the philosophy of doub.entry.bk.keeping. Using your method the total becomes double because all the balances are deemed to be positive numbers.

In summary, some important queries become more difficult to write if you structure your data this way.

Upvotes: 0

Zac Imboden
Zac Imboden

Reputation: 771

Storing debits as negative numbers is a bad idea. In double-entry accounting, a debit to a debit-type account, like AR, is an increase in the AR balance. Conversely, a debit to a credit-type account, like Income, is a decrease in that account's balance. I too have worked with a system where every transaction record had a debit amount field and a credit amount field. I, too, found that wasteful. For our internal billing application, I use transactions that specify a debit account and a credit account, by id. There is another table, a Chart of Accounts, that stores all our accounts. Each account has a type of either debit or credit.

To get a balance on a specific account, the amount is positive if you are debiting a debit account, otherwise you negate the amount.

We have two tables:

ChartOfAccounts

**id    name    code    Type**
------------------------------
1       AR      100     debit
2       Cash    200     debit
3       Income  300     credit

**Transactions**
**id    date    debitAcctId creditAcctId    amount**
------------------------------------------------
1   9/16/15     1           3               100
This is a charge. We debit AR and credit Income

2   9/20/15     2           1               50
This is a payment. We debit Cash and credit AR.

Transaction 1 We debit AR to increase its balance. We credit Income, a credit type account, to increase its balance. AR tells us what we've owed. Income tells us what we've "earned". Our transaction follows the basic accounting principal of debiting one account and crediting another. Debit AR, Credit Income. In this case, both those account balances were increased.

Transaction 2 When we receive a payment, we need to remove that amount from AR, because we are no longer owed it. To remove money from AR, a debit account, we credit the amount to AR. Payment is considered cash, so we want to add the amount to our Cash account. Cash is a debit account, so we debit the payment amount. Again, we obeyed basic accounting by having one debit and one credit: we debit Cash and credit AR.

Now we want to find our AR balance. We want to find all debit and credit transactions to that account. Since transactions store the account id, we need to know that the id for AR is 1. We find transactions that have AR's id of 1 in either their debitAcct or creditAcct fields. Then we must know that AR is a debit type account. The amount of any transaction that debits AR is considered a positive amount. The amount of any transaction that credits AR is considered a negative amount.

Because we are finding transactions that either debit or credit AR, we can check if AR is the debit or the credit account. If the debit, then we leave the positive amount. Otherwise, we negate the amount.

-- find our AR balance from all transactions
-- debiting or crediting our AR account
SELECT
-- if our transactions debit account is AR, which is a debit-type account,
-- our amount should be positive, otherwise we make our amount negative.

IF(Transactions.debitAcctId = 1,amount,-amount) as amount
FROM
Transactions
WHERE
Transactions.debitAcctId = 1
OR
Transactions.creditAcctId = 1

The above sql is not meant as a working example, just the general approach.

The gist of what I'm saying is that you should treat transactions as general ledger entries that debit one account and credit another. If you have accounting actions that need to debit/credit more than two accounts, create more transactions. Treat the amount of the transaction as positive or negative depending on the type of account involved.

Using the above methodology, I have built single sql statements that efficiently show the balance for multiple accounts at one time.

Upvotes: 2

Jim
Jim

Reputation: 11379

I worked with an accounting system used by some big companies. The general ledger transaction table had separate columns for debits and credits. Something like:

customer_id
DebitAmount (decimal)
CreditAmount (decimal)

Only one of the columns would have a value greater than 0, the other was always 0. It doesn't seem very efficient, but it worked. Basically, you have to pick a convention and make your code work with it.

Upvotes: 6

Kevin LaBranche
Kevin LaBranche

Reputation: 21078

The second may be easier BUT if your system becomes more complicated such as needing to track types of debits and/or credits then you may want have a type field. In classic accounting with T-Accounts you have to have matching debit and credit transaction types.

http://www.ehow.com/how_5130376_balance-taccount.html

http://www.cliffsnotes.com/WileyCDA/CliffsReviewTopic/T-Accounts.topicArticleId-21081,articleId-21009.html

I did this in a system once and had a type if you will. Each type represented a right or left side transaction.

Definitely made for harder code but it was a requirement of the system and worked out great.

Upvotes: 3

Doug McClean
Doug McClean

Reputation: 14485

Isn't it generally better to have:

entry_id // PK
date
amount // always positive
debit_account_id // FK to accounts table
credit_account_id // FK to accounts table, not equal to debit_account_id

This way you always have matching double entry bookkeeping?

Some accounts will be customer accounts, one will be the accounts receivable account, etc.

See also this chapter from Martin Fowler's Analysis Patterns book.

Upvotes: 6

manji
manji

Reputation: 47978

The 2nd solution is simpler & more efficient & readable

The 1st one will just add more complication to queries when you want to do some aggregation (sum, avg,...) as you have to translate it to a 'sign'.

An enum column would be of use when there is more categories and/or categories that can not be distinguished by the value only: DEBIT/CREDIT/TAX/...

Upvotes: -1

Erich
Erich

Reputation: 655

The second one is easier, and more efficient. It becomes much easier to query in the future, specifically for balances.

Upvotes: 6

Related Questions