午餐报销申请的建议DB模式

时间:2011-03-09 05:25:04

标签: php mysql database-design schema database-schema

我正在使用jqTouch设计基于网络的智能手机应用程序。我们的想法是:

  • 追踪欠谁的午餐
  • 随着时间的推移追踪债务
  • 将债务从一个人转移到另一个人

这听起来很复杂,但它会为我的午餐伙伴解决一个现实世界的问题,他们经常互相付钱,最后通过Paypal报销。对于那些不这样做的人,他们经常交易债务......例如:

Jason buys lunch for David
David buys lunch for Roslyn
Phillip buys lunch for Roslyn

所以 - 大卫欠杰森,罗斯林欠大卫菲利普。作为债务清偿,大卫将他的债务转交给杰森,罗斯林购买午餐作为大卫的偿还,现在他们甚至是。唯一留下来的人就是我,这对于课程来说非常相似:)

真正的问题是......

如何根据关系数据库表达这一点?我可以跟踪项目支出和用户:

purchases
=========
purch_id
user_id
amount
location

users
=======
user_id
name

我是否将其余部分作为业务逻辑处理?如何跟踪债务上下班?如果我想从中获得有意义的报告,例如随着时间的推移支出,以及给定人员偿还所需的平均时间 - 我需要更复杂的架构!

欢迎任何想法/批评!这是一项有趣的心理练习,绝不算是严重项目。

更新

考虑到对提出的问题缺乏兴趣 - 我发布了一笔赏金!为奖励赏金需要解决的一些问题如下:

  • 如何跟踪运行总计而不必总结可能冗长的事务表
  • 如何跟踪没有过度规范化的交易的元数据(例如上面描述的'减刑'债务特征

5 个答案:

答案 0 :(得分:2)

您建议的大纲听起来不错......从以下关系开始 跟踪人员和交易:

 Person(*PID*, Name)  

其中PID是唯一标识每个人的代理键。

 Transaction(*TID*, Description, Date)  

其中TID是唯一标识您希望跟踪的每笔交易的代理键(例如午餐)。

跟踪谁欠谁:

 Owes(*PIDOwes*, *PIDOwed*, *TID*, Amount)  

其中PIDOwes和PIDOwed是Person关系中的ID。 TID标识交易 负责创建借方及其金额。

每次小组去吃午餐时,都会创建一个新的交易和Owes关系 更新以反映谁欠谁的结果。这就是您的数据库的样子 杰森给大卫买了15.00顿午餐; 大卫购买罗斯林午餐10.00,菲利普购买罗斯林午餐12.00。假设 午餐费用是50:50(你可以选择分开它。重要的是这个 借方分配给每个人):

Person(J, Jason)
Person(D, David)
Person(R, Roslyn)
Person(P, Phillip)

Transaction(1, Lunch, 2011-03-04)
Transaction(2, Lunch, 2011-03-05)
Transaction(3, Lunch, 2011-03-06)

Owes(D, J, 1, 7.50)
Owes(R, D, 2, 5.00)
Owes(R, P, 3, 6.00)

通勤借记:

当大卫将罗斯林的借记交给杰森时,你会记录以下内容 交易:

Transaction(4, CommuteDebit, 2011-03-07)

Owes(R, J, 4,  5.00)
Owes(R, D, 4, -5.00)

确认Roslyn事实上至少欠David大约5.00可能是明智之举 接受这笔交易!

上面的第二个Owes行可能记录为David欠Roslyn 5.00但是我喜欢+/- 方法因为它捕获了这是零和交易的想法。另一个好处是 你可以识别谁开始减记借记,因为他们将永远是PIDOwed Owes行有负数。

当Roslyn拿起Jason的午餐选项卡时,会创建以下事务 花费13.00分50:50:

Transaction(5, Lunch, 2011-03-08)

Owes(J, R, 5, 6.50)

突然事情变得复杂......相互借记累积起来。大卫欠Roslyn和 罗斯林欠大卫。这些借记应该相互抵消,以至于David欠Roslyn 1.50。事情 关于欠款关系的是,除了借记通勤之外,金额只会累积。获取 通过从'B'欠'A'净结算所有'A'欠款'B'来实现平衡。该 Owes关系是交易细节的记录,它不提供运行余额。

计算'Jason owes Roslyn'余额的查询非常简单,如下所示:

select sum(case when PIDOwes = 'J' then
                     +amount
                else
                     -amount
                end)
from Owes
where PIDOwes in ('R', 'J')
  and PIDOwed in ('J', 'R')

此查询将返回1.50 - Jason现在欠Roslyn的金额。

通过迭代上述查询,可以找到Roslyn接下来要吃午餐的人 在Person表中,在每次迭代时替换'J'(Roslyn保持不变)。 当总和为正数时,罗斯林欠了。当总和为负数时, 那个人欠Roslyn。

这样的查询将导致:

Phillip  6.00
Jason   -1.50

所以,罗斯林应该对待菲利普和贾森应该对待罗斯林。

例如,计算个人的总体余额将会完成 通过将所有Owes行与大卫作为PIDOwes进行求和并从中提取它们 大卫是PIDOwed的行总和。

这个方案的基本问题是你必须总结整个Owes关系 为了获得余额。优化的可能道德化可能是维持 平衡关系,例如:

 OwesBal(*PIDOwes*, *PIDOwed*, Balance)

每次将一行添加到Owes关系记录中A欠B一些金额, OwesBal关系更新如下:

set TranAmt to whatever A owes B according to the new Owes row.

select Balance as AowesB
from OwesBal
where PIDOwes = 'A'
  and PIDOwed = 'B'

select Balance as BowesA
from OwesBal
where PIDOwes = 'B'
  and PIDOwed = 'A'

if BowesA < TranAmt then
   TranAmt = TranAmt - BowesA
   BowesA = 0.00
else
   BowesA = BowesA - TranAmt
   TranAmt = 0.00

update OwesBal
set Balance = BowesA
where PIDOwes = 'B'
  and PIDOwed = 'A'

update OwesBal
set Balance = Balance + TranAmt
where PIDOwes = 'A'
  and PIDOwed = 'B'

当然,你需要捕获'not-found'条件并创建初始的OwesBal行 因为午餐伙伴的新组合出现了。

上述会计模型不容易跟踪随时间推移的午餐总支出。它只是 让你跟踪谁欠谁。跟踪总支出可以通过增加来实现 以下与您的模型的关系:

 Share(*PID*, *TID*, Amount)

在填充交易和欠款关系时,将填充共享关系。每个人都会 输入他们的份额。例如:

Share(D, 1, 7.50)
Share(J, 1, 7.50)
Share(R, 2, 5.00)
Share(D, 2, 5.00)
Share(R, 3, 6.00)
Share(P, 3, 6.00)
Share(J, 5, 6.50)
Share(R, 5, 6.50)

不会记录借记通勤等交易。您可以按日期获得费用 加入交易关系。

答案 1 :(得分:1)

您可以使用dleavitt建议的布局,并将lunch_id,creditor_id,debtor_id和amount字段组合为主键。这将允许您使用相同的lunch_id添加另一行,只要至少有一个其他字段更改值。这意味着您可以添加具有相同lunch_id的行,只需更改creditor_id即可将该午餐转交给其他人。同样,您可以选择更改新行中的debtor_id,以将债务从一个人转移到另一个人。通过此设置,您还可以通过添加新行并输入负值来指示付款来实施部分付款。添加这些金额将为我提供所欠金额的总计。这应该足以跟踪变化并仍然保持历史数据。可能的添加可能是日期时间字段,用于检查插入新行的时间。将此字段添加到主键还可以解决有人减欠债务的情况,然后将其转回原始债权人。这里新行将具有不同的日期时间值,因此它将有效。 enter image description here

答案 2 :(得分:1)

有趣的问题 - 让我们做一些假设。

一个人可以为一个或多个其他人购买午餐(至少为了这个应用程序的目的 - 如果他们自己买午餐,我们不在乎)。

所有午餐都有相同的价值 - 我们假设计量单位是“午餐”,而不是“钱”。那么,你在丽思酒店给我买午餐,我在麦当劳给你买午餐,我们就是正方形。

偿还债务的唯一方法是购买午餐。没有现金方式可以摆脱困境,也无法“免除”债务。

当你欠我3个午餐,然后给我买一顿午餐时,你买的午餐和你欠我的午餐之间没有直接的关系 - 午餐可以用来制作。

如果您想保留历史记录和内容,我认为您需要某种类型的交易表。我不认为总结这些交易会产生任何性能影响,除非你为数百万人建立一个解决方案 - 你能解释一下你不喜欢这个吗?

我的提案架构类似于:

**User**
user_id pk
name

**lunch**
lunch_id pk
date

**transaction**
transaction_id pk
user_id
value
lunch_id

因此,当我给你买午餐时,我们在“午餐”表中创建一个新记录,并插入一个包含您的用户ID,值为“-1”的交易以及对午餐ID的引用。我们还为我创建了一个交易,其值为“+1”。

我们可以随时通过将午餐桌加入交易表来查看用户的余额,并查询两个日期之间的午餐日期的所有交易。

我们可以通过添加所有值来查看用户的当前余额。

答案 3 :(得分:0)

事实上,我也在量身思考社会情境。

您想用三个表来表示它:

用户

  • ID
  • 名称

<强>午餐

  • id
  • location
  • 日期

<强>债务

  • lunch_id(午餐)
  • creditor_id(借出的用户)
  • debtor_id(借用的用户)
  • 金额

每个“午餐”都可以有零个或多个与之相关的“债务”。

每个“债务”都有两个用户(债权人和债务人)和金额。

此设置应该可以很容易地确定每个人的总体平衡以及任何两个用户之间的平衡。

答案 4 :(得分:0)

我在银行工作,我认为对你的案例最适合的方法是建立一个基于一种信用的系统。我不确定午餐的价格是否与问题有关。给朋友买了一块带有异国水果的小蛋糕。你给他/她买了一个McChiken。是同样的债务?无论情况如何(午餐的价格与否都很重要),信用体系应该是一件好事。我要设计的桌子能够让人觉得有些午餐比其他午餐更有价值。否则,只考虑所有午餐的一个积分。

t001_partners
prt_id: bigInt
prt_name: string
prt_balance: int

t002_lunch
lnc_id: bigInt
lnc_description: string
lnc_store: string
lnc_credits: int

t003_lunch_x_partner
lxp_buyer: bigint (FK)
lxp_receiver: bigint(FK)
lxp_date: dateTime
lxp_quantity: int

现在您的系统能够(使用不同类型的查询):

  1. 追踪每位合作伙伴购买/收到的午餐,地点和数量;

  2. 对于那些如果合伙人有信用的人,他不负债 有权获得午餐;

  3. 如果上一个功能不满足您的第三个要求,您应该创建一个程序,通过特殊ID在合作伙伴之间转移信用 t002_lunch表在t003_lunch_x_partner表中注册。

  4. 如果所有午餐都具有相同的值,则系统适合。如果午餐会有不同的信用,系统就适合。您甚至可以使用货币作为信用创建系统(只需将表t002中的lnc_credits和t001中的prt_balance更改为浮点或货币类型)。

  5. 为了满足您的挑战(总共没有总和一张大表)我在t001中创建了一个字段prt_balance。当新事务发生时更新此余额。如果您在3中实施建议,那么在没有额外规范化的情况下引导交汇很容易。

  6. 随意在此规范中发表评论,询问或提及。

    我希望我能帮到你。给我买个午餐! ;)