哪个是事务表的更好架构:
customer_id
type (enum, 'Debit' or 'Credit')
amount (decimal, unsigned)
或
customer_id
amount (decimal, signed... negative numbers are debits, positive numbers are credits)
第二种解决方案似乎更简单,但我担心我错过了一些潜在的陷阱。
答案 0 :(得分:6)
第二个更容易,更有效率。将来查询变得更加容易,特别是对于余额。
答案 1 :(得分:4)
通常情况下更好:
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
这样您总是匹配double entry bookkeeping?
有些帐户是客户帐户,其中一个是应收帐款帐户等。
答案 2 :(得分:4)
<强>背景强>
借记代表您拥有的东西,而抵免代表其他人拥有的东西。它们不是相同的维度单位,不应存储在同一个数据库列中。使用符号位来表示借方或贷方是对double-entry bookkeeping如何运作的过度简化;尽管如此,这种过度简化仍然会出现在低端和自制的会计软件包中,可能是因为这是普通人对会计的看法。 [1]
我发现通过复式记账使软件开发速度最快的最简单的方法是注意会计系统中的数字不是标量 - 它是一个向量。向量元素由维度轴(借方或贷方)以及大小(有符号的固定位置小数)组成。 [2]
<强>解决方案强>
您的第一个解决方案代表数据的矢量性质,并遵循公认的会计惯例,但仍然将幅度元素存储在同一列中,无论它适用于哪个轴。这使得SELECT语句更加复杂。
最好将借方和贷方金额分成不同的列;这消除了对轴(枚举)列的需要,简化了SQL,可能是性能改进,并且是更传统的方法。
你的第二个解决方案(超载标志位代表借方或贷方)每次看到它都会让我感到害怕,因为我无法确定建筑师是否在某种程度上补偿丢失的尺寸信息,或者只是不明白会计数据的矢量性质。从我在SO上看到的情况来看,显然有很多会计软件包以这种方式编写,但它会产生复杂,脆弱,较慢的代码和数据结构,所有这些都是为了节省一小部分数据库空间。
<强>来源
曾几何时,我是一家国际银行的交易系统工程师。角落案例不好,代码简单好。
<强>脚注强>
[1]:我认为人们偶然会想到“负值是借方”,部分原因在于银行的运作方式;银行使用的语言让人们对借记的错误印象。从银行的角度来看,您的支票账户是其他人拥有的 - 它有贷方余额。当您将钱存入银行时,他们会告诉您他们“记入”您的帐户,当您退出时,他们会说“借记”。但这一切都来自银行的观点,所以它都是倒退的。在您自己的会计系统中(如果您有),借记您的支票账户意味着增加余额,而信用额度是减少。从您的角度来看,该帐户具有借记余额,因为是您拥有的。
[2]:如果您的语言支持固定小数位复数,它们可能是处理会计数据的便捷方式;借记可能在实轴上,而信用可能是想象的。这创造了一些有趣的属性;平衡的条目集将具有45度的相位角,依此类推。但除非您的数据库引擎本身支持复杂的数字,否则您最终会将实际和复杂的组件分成两列进行存储,这些列将被称为“借记”和“信用”。
答案 3 :(得分:3)
第二个可能更容易但是如果你的系统变得更复杂,例如需要跟踪借方和/或信用类型,那么你可能想要一个类型字段。在使用T-Accounts的经典会计中,您必须具有匹配的借记和贷记交易类型。
http://www.ehow.com/how_5130376_balance-taccount.html
我在一个系统中做了一次,如果你愿意,我有一个类型。每种类型代表右侧或左侧交易。
绝对是为更难的代码而制作的,但它是系统的要求并且效果很好。
答案 4 :(得分:3)
我使用的是一些大公司使用的会计系统。总帐交易表具有单独的借方和贷方列。类似的东西:
customer_id
DebitAmount (decimal)
CreditAmount (decimal)
只有一列的值大于0,另一列总是0.它看起来效率不高,但它有效。基本上,您必须选择一个约定并使代码与它一起工作。
答案 5 :(得分:2)
如上所述,第二个模式不支持基本会计原则。第一个模式是存储数据的一种方式,另一个有意义的是有两个单独的信用和借记列。
我真的很想评论的是Doug McClean的回答。您只能借记和贷记一个帐户,这样可以确保正确的余额。然而,实际上,通常涉及多个帐户,例如,税收。因此,该模型不能很好地捕获会计。
答案 6 :(得分:1)
将借记存储为负数是一个坏主意。在复式记帐中,借记到借记型帐户(如AR)是AR余额中的增加。相反,对于信用类型帐户的借记(如收入)是该帐户余额中的减少。我也使用过系统,其中每个交易记录都有借方金额字段和贷方金额字段。我也发现浪费了。对于我们的内部结算应用程序,我使用指定借方帐户和信用帐户的交易(按ID)。还有另一个表,即会计科目表,用于存储我们的所有帐户。每个帐户都有一种借记或贷记类型。
要获取特定帐户的余额,如果您要借记借记帐户,则金额为正数,否则您将取消金额。
我们有两张桌子:
<强> 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.
交易1 我们借记AR以增加其余额。我们将信用收入(Credit)用于增加余额。 AR告诉我们我们欠的是什么。收入告诉我们我们“赚取了什么”。我们的交易遵循从一个账户借记和贷记另一个账户的基本会计原则。借记AR,信用收入。在这种情况下,这两个账户余额都增加了。
交易2 当我们收到付款时,我们需要从AR中删除该金额,因为我们不再欠它。要从AR(借记帐户)中移除资金,我们会将金额记入AR。付款被视为现金,因此我们希望将金额添加到我们的现金帐户。现金是借记帐户,因此我们会从付款金额中扣除。同样,我们通过一笔借记和一笔贷记来遵守基本会计:我们借记现金和贷方AR。
现在我们想要找到我们的AR平衡。我们希望找到该帐户的所有借记和贷记交易。 由于交易存储了账户ID,我们需要知道AR的ID是1.我们在debitAcct或creditAcct字段中找到AR的id为1的交易。 然后我们必须知道AR是借记类型帐户。任何扣除AR的交易金额都被视为正数。任何记入AR的交易金额都被视为负数。
因为我们正在查找借记或贷记AR的交易,我们可以检查AR是借方还是贷方账户。如果借记,那么我们留下正数。否则,我们否定金额。
-- 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
上面的sql并不是一个工作示例,只是一般方法。
我所说的要点是,您应该将交易视为借记一个账户并贷记另一个账户的总账分录。如果您有需要借记/贷记两个以上帐户的会计操作,请创建更多交易。 根据所涉及的账户类型,将交易金额视为正数或负数。
使用上述方法,我构建了单个sql语句,可以一次有效地显示多个帐户的余额。
答案 7 :(得分:0)
第二种解决方案更简单&amp;效率更高可读
当你想要进行一些聚合(sum,avg,...)时,第一个只会为查询添加更复杂的内容,因为你必须将它转换为'符号'。
当有更多的类别和/或类别无法仅通过值区分时,将使用枚举列:DEBIT / CREDIT / TAX /...
答案 8 :(得分:0)
我建议Debits和credit可能必须保留为具有'unsigned'属性的单独列。
当借方超过贷方时,结果计算的余额列(即累计借方总和 - 累计贷方总和列)必须显示带负号的负值( - )。
在制定查询以在给定日期获得试算平衡时,根据doub.entry.bk.keeping的原则,这应该为零。使用您的方法,总数变为双倍,因为所有余额都被视为正数。
总之,如果以这种方式构建数据,一些重要的查询会变得更难编写。