Mysql 创建透支对账单

Mysql 创建透支对账单,mysql,sql,database,Mysql,Sql,Database,我目前一直在思考如何创建一个报表,显示特定委员会的每日透支报表 我有以下内容:委员会、用户、市场、市场交易、用户存款 市场交易每天运行,减少用户帐户余额。当账户余额为0时,用户将进入负透支状态。当用户存款时,他们的帐户余额会增加 我用下表来说明交易和存款是如何存储的。 如果我撤销今天的交易,我就能够得到用户昨天的账户余额,但制定查询以获得每日OD金额才是问题所在 使用者 用户id 名称 帐户余额 1. 井 -5 2. 詹姆斯 100 3. 快乐 10 4. 蒙比 -300 这其实没那么难,但你问

我目前一直在思考如何创建一个报表,显示特定委员会的每日透支报表

我有以下内容:委员会、用户、市场、市场交易、用户存款

市场交易每天运行,减少用户帐户余额。当账户余额为0时,用户将进入负透支状态。当用户存款时,他们的帐户余额会增加

我用下表来说明交易和存款是如何存储的。 如果我撤销今天的交易,我就能够得到用户昨天的账户余额,但制定查询以获得每日OD金额才是问题所在

使用者

用户id 名称 帐户余额 1. 井 -5 2. 詹姆斯 100 3. 快乐 10 4. 蒙比 -300
这其实没那么难,但你问的方式让人很难理解

此外,您的预期结果应该与您提供的数据相匹配

编辑:以前的解决方案是错误的-如果每个用户/日期有多个事件,它会多次计算提款和存款

从每天交换总数开始,如

select user_id, date, sum(amount) exchanged_on_day from (
            select user_id, date, amount amount from deposits 
  union all select user_id, date, -amount_tendered amount from transactions
) d 
group by user_id, date
order by user_id, date;
以下内容仅在有任何存款或取款的日期获取帐户状态。 要获得所有日期的结果,而不仅仅是那些帐户移动的结果,您只需更改交叉连接部分,就可以得到一个包含所有日期的表,但我偏离了主题

select dates.date, c.council_id, u.name username
  , u.account_bal - sum(case when e.date >= dates.date then e.exchanged_on_day else 0 end) as amount_on_start_of_day
  , u.account_bal - sum(case when e.date > dates.date then e.exchanged_on_day else 0 end) as amount_on_end_of_day
from councils c
inner join markets m on c.council_id=m.council_id
inner join market_user_link mul on m.market_id=mul.market_id
inner join users u on mul.user_id=u.user_id
left  join (
  select user_id, date, sum(amount) exchanged_on_day from (
              select user_id, date, amount amount from deposits 
    union all select user_id, date, -amount_tendered amount from transactions
  ) d group by user_id, date
) e on u.user_id=e.user_id --exchange on each Day
cross join (select distinct date from (select date from deposits union select date from transactions) datesInternal) dates --all days that had a transaction
group by dates.date, c.council_id, u.name, u.account_bal
order by dates.date desc, c.council_id, u.name;
从那里你可以重新安排得到你想要的结果

select date, council_id
, sum(case when amount_on_start_of_day<0 then amount_on_start_of_day else 0 end) o_d_amount_start
, sum(case when amount_on_end_of_day<0 then amount_on_end_of_day else 0 end) o_d_amount_end
from (
  select dates.date, c.council_id, u.name username
  , u.account_bal - sum(case when e.date >= dates.date then e.exchanged_on_day else 0 end) as amount_on_start_of_day
  , u.account_bal - sum(case when e.date > dates.date then e.exchanged_on_day else 0 end) as amount_on_end_of_day
  from councils c
  inner join markets m on c.council_id=m.council_id
  inner join market_user_link mul on m.market_id=mul.market_id
  inner join users u on mul.user_id=u.user_id
  left  join (
    select user_id, date, sum(amount) exchanged_on_day from (
                select user_id, date, amount amount from deposits 
      union all select user_id, date, -amount_tendered amount from transactions
    ) d group by user_id, date
  ) e on u.user_id=e.user_id --exchange on each Day
  cross join (select distinct date from (select date from deposits union select date from transactions) datesInternal) dates --all days that had a transaction
  group by dates.date, c.council_id, u.name, u.account_bal
) result
group by date, council_id
order by date;

您可以在

上查看,基本上,该查询将用户映射到议会,计算用户的透支周期,它们在议会上聚合。我假设起始余额的日期为“2021-04-01”月初,也可以是期末余额,见下文,根据需要进行更改。此外,负的起始余额也算作透支。为了简单和调试,查询分为若干步骤

  with uc as (
   select distinct m.council_id, mul.user_id
   from markets m 
   join market_user_link mul on m.market_id = mul.market_id
  ),
  user_running_total as (
    select user_id, date, 
     coalesce(lead(date) over(partition by user_id order by date) - interval 1 day, date) nxt,
     sum(sum(s)) over(partition by user_id order by date) rt
    from (
       select user_id, date, -amount_tendered s
       from transactions 
       union all
       select user_id, date, amount
       from deposits
       union all
       select user_id, se.d, se.s
       from users
       cross join lateral (
            select date(NOW() + interval 1 day) d, 0 s
            union all
            select '2021-04-01' d, account_bal
        ) se
    ) t
    group by user_id, date
  ),
  user_overdraft as (
     select user_id, date, nxt, least(rt, 0) ovd
     from user_running_total 
     where date <= date(NOW()) 
  ),
  dates as (
     select date
     from user_overdraft
     union 
     select nxt
     from user_overdraft
  ),
  council__overdraft as ( 
     select uc.council_id, d.date, sum(uo.ovd) total_overdraft, lag(sum(uo.ovd), 1, sum(uo.ovd) - 1) over(partition by uc.council_id order by d.date) prev_ovd
     from uc
     cross join dates d
     join user_overdraft uo on uc.user_id = uo.user_id and d.date between uo.date and uo.nxt
     group by uc.council_id, d.date
  )
  select council_id, date, total_overdraft
  from council__overdraft
  where total_overdraft <> prev_ovd
  order by date, council_id  
按日期排序的存款,为最后一个日期添加了额外的行

id  user_id amount  date
3   3   5   2021-04-25
4   4   5   2021-04-25
1   1   5   2021-04-26
2   3   10  2021-04-26
5   3   73  2021-05-06
按日期排序的事务,请注意添加的行,以说明正在运行的总计

id  user_id amount_tendered date
5   4   50  2021-04-25
2   2   10  2021-04-26
3   3   15  2021-04-26
1   1   5   2021-04-27
4   3   17  2021-04-27
理事会

council_id  name
1   a
2   b
3   c
市场

market_id   name    council_id
1   x   3
2   y   1
3   z   2
市场用户链接

id  market_id   user_id
1   1   3
2   2   2
3   3   1
4   3   4
查询输出为

理事会 日期 透支 1. 2021-04-01 0 2. 2021-04-01 -305 3. 2021-04-01 0 2. 2021-04-25 -350 2. 2021-04-26 -345 2. 2021-04-27 -350 3. 2021-04-27 -7 3. 2021-05-06 0
MySQL!=SQL Server-请更正您的标记。@您的输出与给定的示例数据不匹配,您的示例数据是4月24日及以后的,而您的输出显示的是4月1日和2日,而不是usefull@eshirvana,好吧,我希望它能帮助说明最终目标,但我已经更新了它们以适应日期您的MARKET\u USER\u LINK.MARKET\u id列似乎是错误的;它应该包含整数。您应该使用诸如db-fiddle.com之类的站点来定义和初始化这些具有正确数据的表。有关输出中显示的透支金额,请参见。@Wells,我看不出如何使用给定的样本数据计算透支金额。用英语解释一个给定的示例数据hey@Serg,感谢您提供额外的测试用例,在我的解决方案中发现了一个错误。我想你弄错了,因为问题是,如果我撤销今天的交易,我就能得到用户昨天的账户余额——这意味着起始余额的日期在事件表的最后日期之后。@BrunoCanettieri。。添加了当users表持有结账时的查询版本,现在是余额。谢谢你的观点。嘿@Serg,如果最后一天透支是-305,那么前一天不可能是-5,因为没有足够大的操作来实现这一点。问题是,在您的内部查询中,当没有操作时,没有用户/天的rt余额行-然后您的最终查询将其视为零。@BrunoCanettieri,。。谢谢,没错。添加了日期步骤,更多测试数据。我添加了更多示例数据,您可能希望使用新数据测试您的解决方案。@Serg,谢谢。不同的是,你在每天的开始显示透支。我的查询中的一个简单更改会得到与您相同的结果,使用>as explained correction,使用>=您的意思是当e.date>=dates.date时的sumcase吗?不,约会还是不同的。我的查询列出了一天结束时的透支。例如,委员会3由单个用户3组成。因此,当用户3在2021-04-27花费17英镑时,我的查询将列出委员会3:2021-04-26-46; 2021-04-27 -63. 当用户在2021-05-06存款73时,我的查询显示那天的透支为0。您的查询在一天结束时列出了结果,这是对的,我的错误。但我们的结果是一样的。我已经更新了我的答案,以便db fiddle使用与您提供的相同的测试数据。现在的差异只是因为您添加了一个额外的日期2021-04-01。还是我没有看到明显的东西?
id  market_id   user_id
1   1   3
2   2   2
3   3   1
4   3   4