计算租户的水费

故事背景:租住的房子是套四的,人员流动性也比较大,每次新租客搬入老租客搬走时总有人会对水气费用的结算产生争执,所以我写了这个代码来分析每个租客用水量,从生活出发,又回到生活中去.

 

计算每个租户需要结清的用水量
(每个房间无论居住几人均按一人一间计算)

当一个租户居住时,用水量完全由其一人承担,当有新租户入住时,老租户均需要结清当前用水量,
并且后续水费由此时入住的所有租户均摊
例如q在7.1入住,表值750.1,w在7.2日入住,表值760.1,则q用水量为10,w用水量=0
e在7.3入住,表值770.1,则q用水量=(10+(770.1-760.1)/2 )= 15 ,w 用水量为 (0 +(770.1-760.1)/2  ) = 15,e用水量=0
以此类推,若租户一直没搬走,止租日取结算日,表止值取当日表值,
每一次新租户的搬入及老租户的搬走,均需要在该时点进行结算

后续也有一些深思,费用的计算应该需要做拉链,不同时期的价格有上浮和下沉,计算还可以细化,

又假如这是一间酒店,酒店的房间数无穷多,计算的数据量达到TB级别时,又如何能在短时间内得到结果,代码是否可以优化,以后遇到了再想吧~~~

-- 000 原始数据
 

租户id   租户名称     起租日 止租日  起租日表值    止租日表值       水费单价/元
user_id  user_name    sdt    edt     sv_w          ev_w           price_w

select * from jghy;
+---------+-----------+------------+------------+--------+--------+---------+
| user_id | user_name | sdt        | edt        | sv_w   | ev_w   | price_w |
+---------+-----------+------------+------------+--------+--------+---------+
|       1 | q         | 2021-07-01 | 2021-11-01 |  950.1 | 1200.1 |     3.5 |
|       2 | w         | 2021-07-02 | NULL       |  960.1 | NULL   |     3.5 |
|       3 | e         | 2021-07-03 | 2021-10-01 |  970.1 | 1100.1 |     3.5 |
|       4 | r         | 2021-08-01 | NULL       |  975.1 | NULL   |     3.5 |
|       5 | t         | 2021-09-01 | NULL       |  980.1 | NULL   |     3.5 |
|       6 | y         | 2021-10-01 | 2021-11-15 | 1100.1 | 1250.1 |     3.5 |
|       7 | u         | 2021-09-15 | NULL       |  990.1 | NULL   |     3.5 |
+---------+-----------+------------+------------+--------+--------+---------+


-- ----------------------------
-- Table structure for jghy
-- ----------------------------
DROP TABLE IF EXISTS `jghy`;
CREATE TABLE `jghy`  (
  `user_id` int(11) DEFAULT NULL,
  `user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  `sdt` date DEFAULT NULL,
  `edt` date DEFAULT NULL,
  `sv_w` double DEFAULT NULL,
  `ev_w` double DEFAULT NULL,
  `price_w` double DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of jghy
-- ----------------------------
INSERT INTO `jghy` VALUES (1, 'q', '2021-07-01', '2021-11-01', 950.1, 1200.1, 3.5);
INSERT INTO `jghy` VALUES (2, 'w', '2021-07-02', NULL, 960.1, NULL, 3.5);
INSERT INTO `jghy` VALUES (3, 'e', '2021-07-03', '2021-10-01', 970.1, 1100.1, 3.5);
INSERT INTO `jghy` VALUES (4, 'r', '2021-08-01', NULL, 975.1, NULL, 3.5);
INSERT INTO `jghy` VALUES (5, 't', '2021-09-01', NULL, 980.1, NULL, 3.5);
INSERT INTO `jghy` VALUES (6, 'y', '2021-10-01', '2021-11-15', 1100.1, 1250.1, 3.5);
INSERT INTO `jghy` VALUES (7, 'u', '2021-09-15', NULL, 990.1, NULL, 3.5);

-- 001 所有租户起止时点

select distinct user_id,sdt,IFNULL(edt,(select max(edt) from jghy)) edt 
from jghy;

+---------+------------+------------+
| user_id | sdt        | edt        |
+---------+------------+------------+
|       1 | 2021-07-01 | 2021-11-01 |
|       2 | 2021-07-02 | 2021-11-15 |
|       3 | 2021-07-03 | 2021-10-01 |
|       4 | 2021-08-01 | 2021-11-15 |
|       5 | 2021-09-01 | 2021-11-15 |
|       6 | 2021-10-01 | 2021-11-15 |
|       7 | 2021-09-15 | 2021-11-15 |
+---------+------------+------------+

-- 002 所有需要进行计算的时点 

select distinct sdt dt from jghy
where sdt  is not null 
union 
select distinct edt dt from jghy
where edt is not null ;

+------------+
| dt         |
+------------+
| 2021-07-01 |
| 2021-07-02 |
| 2021-07-03 |
| 2021-08-01 |
| 2021-09-01 |
| 2021-10-01 |
| 2021-09-15 |
| 2021-11-01 |
| 2021-11-15 |
+------------+

-- 003 每个租户对应的计算时点及表值
 

select a.user_id,b.dt,ifnull(c.sv_w,d.ev_w) val
from (select distinct user_id,sdt,IFNULL(edt,(select max(edt) from jghy)) edt from jghy) a 
left join (select distinct sdt dt from jghy
where sdt  is not null 
union 
select distinct edt dt from jghy
where edt is not null ) b  on  b.dt BETWEEN a.sdt and a.edt
left join jghy c on b.dt = c.sdt 
left join jghy d on b.dt = d.edt 
ORDER BY a.user_id,b.dt ;

+---------+------------+--------+
| user_id | dt         | val    |
+---------+------------+--------+
|       1 | 2021-07-01 |  950.1 |
|       1 | 2021-07-02 |  960.1 |
|       1 | 2021-07-03 |  970.1 |
|       1 | 2021-08-01 |  975.1 |
|       1 | 2021-09-01 |  980.1 |
|       1 | 2021-09-15 |  990.1 |
|       1 | 2021-10-01 | 1100.1 |
|       1 | 2021-11-01 | 1200.1 |
|       2 | 2021-07-02 |  960.1 |
|       2 | 2021-07-03 |  970.1 |
|       2 | 2021-08-01 |  975.1 |
|       2 | 2021-09-01 |  980.1 |
|       2 | 2021-09-15 |  990.1 |
|       2 | 2021-10-01 | 1100.1 |
|       2 | 2021-11-01 | 1200.1 |
|       2 | 2021-11-15 | 1250.1 |
|       3 | 2021-07-03 |  970.1 |
|       3 | 2021-08-01 |  975.1 |
|       3 | 2021-09-01 |  980.1 |
|       3 | 2021-09-15 |  990.1 |
|       3 | 2021-10-01 | 1100.1 |
|       4 | 2021-08-01 |  975.1 |
|       4 | 2021-09-01 |  980.1 |
|       4 | 2021-09-15 |  990.1 |
|       4 | 2021-10-01 | 1100.1 |
|       4 | 2021-11-01 | 1200.1 |
|       4 | 2021-11-15 | 1250.1 |
|       5 | 2021-09-01 |  980.1 |
|       5 | 2021-09-15 |  990.1 |
|       5 | 2021-10-01 | 1100.1 |
|       5 | 2021-11-01 | 1200.1 |
|       5 | 2021-11-15 | 1250.1 |
|       6 | 2021-10-01 | 1100.1 |
|       6 | 2021-11-01 | 1200.1 |
|       6 | 2021-11-15 | 1250.1 |
|       7 | 2021-09-15 |  990.1 |
|       7 | 2021-10-01 | 1100.1 |
|       7 | 2021-11-01 | 1200.1 |
|       7 | 2021-11-15 | 1250.1 |
+---------+------------+--------+

-- 004 每个计算时点时租户明细及表值
 

select GROUP_CONCAT(tmp1.user_id) users,tmp1.dt, tmp1.val from(
select a.user_id,b.dt,ifnull(c.sv_w,d.ev_w) val
from (select distinct user_id,sdt,IFNULL(edt,(select max(edt) from jghy)) edt from jghy) a 
left join (select distinct sdt dt from jghy
where sdt  is not null 
union 
select distinct edt dt from jghy
where edt is not null ) b  on  b.dt BETWEEN a.sdt and a.edt
left join jghy c on b.dt = c.sdt 
left join jghy d on b.dt = d.edt 
ORDER BY a.user_id,b.dt ) tmp1 
GROUP BY 2,3 ;

+---------------+------------+--------+
| users         | dt         | val    |
+---------------+------------+--------+
| 1             | 2021-07-01 |  950.1 |
| 1,2           | 2021-07-02 |  960.1 |
| 2,3,1         | 2021-07-03 |  970.1 |
| 1,2,3,4       | 2021-08-01 |  975.1 |
| 1,2,3,4,5     | 2021-09-01 |  980.1 |
| 1,2,3,4,5,7   | 2021-09-15 |  990.1 |
| 7,1,2,3,4,5,6 | 2021-10-01 | 1100.1 |
| 2,4,5,6,7,1   | 2021-11-01 | 1200.1 |
| 2,4,5,6,7     | 2021-11-15 | 1250.1 |
+---------------+------------+--------+

-- 005 增加序号,为计算做准备
 

select (@i:=@i+1) id ,tmp2.* from (
select GROUP_CONCAT(tmp1.user_id) users,tmp1.dt, tmp1.val from(
select a.user_id,b.dt,ifnull(c.sv_w,d.ev_w) val
from (select distinct user_id,sdt,IFNULL(edt,(select max(edt) from jghy)) edt from jghy) a 
left join (select distinct sdt dt from jghy
where sdt  is not null 
union 
select distinct edt dt from jghy
where edt is not null ) b  on  b.dt BETWEEN a.sdt and a.edt
left join jghy c on b.dt = c.sdt 
left join jghy d on b.dt = d.edt 
ORDER BY a.user_id,b.dt ) tmp1 
GROUP BY 2,3 ) tmp2 , (select @i:=0) rows ;


+----+---------------+------------+--------+
| id | users         | dt         | val    |
+----+---------------+------------+--------+
|  1 | 1             | 2021-07-01 |  950.1 |
|  2 | 1,2           | 2021-07-02 |  960.1 |
|  3 | 1,3,2         | 2021-07-03 |  970.1 |
|  4 | 2,4,1,3       | 2021-08-01 |  975.1 |
|  5 | 2,4,1,5,3     | 2021-09-01 |  980.1 |
|  6 | 2,4,7,1,5,3   | 2021-09-15 |  990.1 |
|  7 | 6,2,4,7,1,5,3 | 2021-10-01 | 1100.1 |
|  8 | 6,2,4,7,1,5   | 2021-11-01 | 1200.1 |
|  9 | 6,2,4,7,5     | 2021-11-15 | 1250.1 |
+----+---------------+------------+--------+

-- 006 计算用户所在时间节点用水量及整体平均分摊量 

select tmp4.id,tmp3.users,tmp4.dt,tmp3.val,(tmp4.val -tmp3.val),(tmp4.val -tmp3.val)/LENGTH(REPLACE(tmp3.users,',','')) val_u  from (
select (@i:=@i+1) id ,tmp2.* from (
select GROUP_CONCAT(tmp1.user_id) users,tmp1.dt, tmp1.val from(
select a.user_id,b.dt,ifnull(c.sv_w,d.ev_w) val
from (select distinct user_id,sdt,IFNULL(edt,(select max(edt) from jghy)) edt from jghy) a 
left join (select distinct sdt dt from jghy
where sdt  is not null 
union 
select distinct edt dt from jghy
where edt is not null ) b  on  b.dt BETWEEN a.sdt and a.edt
left join jghy c on b.dt = c.sdt 
left join jghy d on b.dt = d.edt 
ORDER BY a.user_id,b.dt ) tmp1 
GROUP BY 2,3 ) tmp2 , (select @i:=0) rows ) tmp3 
left join (
select (@o:=@o+1) id ,tmp2.* from (
select GROUP_CONCAT(tmp1.user_id) users,tmp1.dt, tmp1.val from(
select a.user_id,b.dt,ifnull(c.sv_w,d.ev_w) val
from (select distinct user_id,sdt,IFNULL(edt,(select max(edt) from jghy)) edt from jghy) a 
left join (select distinct sdt dt from jghy
where sdt  is not null 
union 
select distinct edt dt from jghy
where edt is not null ) b  on  b.dt BETWEEN a.sdt and a.edt
left join jghy c on b.dt = c.sdt 
left join jghy d on b.dt = d.edt 
ORDER BY a.user_id,b.dt ) tmp1 
GROUP BY 2,3 ) tmp2 , (select @o:=0) rows ) tmp4 on tmp3.id = tmp4.id -1 
where tmp4.id is not null
ORDER BY tmp3.id;

+----+---------------+------------+--------+----------------------+--------------------+
| id | users         | dt         | val    | (tmp4.val -tmp3.val) | val_u              |
+----+---------------+------------+--------+----------------------+--------------------+
|  2 | 1             | 2021-07-02 |  950.1 |                   10 |                 10 |
|  3 | 2,1           | 2021-07-03 |  960.1 |                   10 |                  5 |
|  4 | 3,2,1         | 2021-08-01 |  970.1 |                    5 | 1.6666666666666667 |
|  5 | 3,2,4,1       | 2021-09-01 |  975.1 |                    5 |               1.25 |
|  6 | 1,5,3,2,4     | 2021-09-15 |  980.1 |                   10 |                  2 |
|  7 | 1,5,3,2,4,7   | 2021-10-01 |  990.1 |   109.99999999999989 | 18.333333333333314 |
|  8 | 7,1,5,3,6,2,4 | 2021-11-01 | 1100.1 |                  100 | 14.285714285714286 |
|  9 | 7,1,5,6,2,4   | 2021-11-15 | 1200.1 |                   50 |  8.333333333333334 |
+----+---------------+------------+--------+----------------------+--------------------+

-- 007 每人每时点需要结算的用水量

select substring_index(substring_index(tmp5.users,',',b.help_topic_id+1),',',-1) as user_id ,tmp5.dt,tmp5.val_u  
from  (

select tmp4.id,tmp3.users,tmp4.dt,tmp3.val,(tmp4.val -tmp3.val),(tmp4.val -tmp3.val)/LENGTH(REPLACE(tmp3.users,',','')) val_u  from (
select (@i:=@i+1) id ,tmp2.* from (
select GROUP_CONCAT(tmp1.user_id) users,tmp1.dt, tmp1.val from(
select a.user_id,b.dt,ifnull(c.sv_w,d.ev_w) val
from (select distinct user_id,sdt,IFNULL(edt,(select max(edt) from jghy)) edt from jghy) a 
left join (select distinct sdt dt from jghy
where sdt  is not null 
union 
select distinct edt dt from jghy
where edt is not null ) b  on  b.dt BETWEEN a.sdt and a.edt
left join jghy c on b.dt = c.sdt 
left join jghy d on b.dt = d.edt 
ORDER BY a.user_id,b.dt ) tmp1 
GROUP BY 2,3 ) tmp2 , (select @i:=0) rows ) tmp3 
left join (
select (@o:=@o+1) id ,tmp2.* from (
select GROUP_CONCAT(tmp1.user_id) users,tmp1.dt, tmp1.val from(
select a.user_id,b.dt,ifnull(c.sv_w,d.ev_w) val
from (select distinct user_id,sdt,IFNULL(edt,(select max(edt) from jghy)) edt from jghy) a 
left join (select distinct sdt dt from jghy
where sdt  is not null 
union 
select distinct edt dt from jghy
where edt is not null ) b  on  b.dt BETWEEN a.sdt and a.edt
left join jghy c on b.dt = c.sdt 
left join jghy d on b.dt = d.edt 
ORDER BY a.user_id,b.dt ) tmp1 
GROUP BY 2,3 ) tmp2 , (select @o:=0) rows ) tmp4 on tmp3.id = tmp4.id -1 
where tmp4.id is not null
ORDER BY tmp3.id ) tmp5
join   mysql.help_topic b on b.help_topic_id < (length(tmp5.users) - length(replace(tmp5.users,',',''))+1);

+---------+------------+--------------------+
| user_id | dt         | val_u              |
+---------+------------+--------------------+
| 1       | 2021-07-02 |                 10 |
| 2       | 2021-07-03 |                  5 |
| 1       | 2021-07-03 |                  5 |
| 3       | 2021-08-01 | 1.6666666666666667 |
| 2       | 2021-08-01 | 1.6666666666666667 |
| 1       | 2021-08-01 | 1.6666666666666667 |
| 1       | 2021-09-01 |               1.25 |
| 3       | 2021-09-01 |               1.25 |
| 2       | 2021-09-01 |               1.25 |
| 4       | 2021-09-01 |               1.25 |
| 5       | 2021-09-15 |                  2 |
| 4       | 2021-09-15 |                  2 |
| 1       | 2021-09-15 |                  2 |
| 3       | 2021-09-15 |                  2 |
| 2       | 2021-09-15 |                  2 |
| 1       | 2021-10-01 | 18.333333333333314 |
| 5       | 2021-10-01 | 18.333333333333314 |
| 4       | 2021-10-01 | 18.333333333333314 |
| 7       | 2021-10-01 | 18.333333333333314 |
| 3       | 2021-10-01 | 18.333333333333314 |
| 2       | 2021-10-01 | 18.333333333333314 |
| 4       | 2021-11-01 | 14.285714285714286 |
| 7       | 2021-11-01 | 14.285714285714286 |
| 1       | 2021-11-01 | 14.285714285714286 |
| 6       | 2021-11-01 | 14.285714285714286 |
| 2       | 2021-11-01 | 14.285714285714286 |
| 5       | 2021-11-01 | 14.285714285714286 |
| 3       | 2021-11-01 | 14.285714285714286 |
| 7       | 2021-11-15 |  8.333333333333334 |
| 1       | 2021-11-15 |  8.333333333333334 |
| 2       | 2021-11-15 |  8.333333333333334 |
| 4       | 2021-11-15 |  8.333333333333334 |
| 5       | 2021-11-15 |  8.333333333333334 |
| 6       | 2021-11-15 |  8.333333333333334 |
+---------+------------+--------------------+

-- 008 按租户进行汇总,计算每个租户使用的用水量

select tmp6.user_id,sum(tmp6.val_u) val_u from (

select substring_index(substring_index(tmp5.users,',',b.help_topic_id+1),',',-1) as user_id ,tmp5.dt,tmp5.val_u  
from  (

select tmp4.id,tmp3.users,tmp4.dt,tmp3.val,(tmp4.val -tmp3.val),(tmp4.val -tmp3.val)/LENGTH(REPLACE(tmp3.users,',','')) val_u  from (
select (@i:=@i+1) id ,tmp2.* from (
select GROUP_CONCAT(tmp1.user_id) users,tmp1.dt, tmp1.val from(
select a.user_id,b.dt,ifnull(c.sv_w,d.ev_w) val
from (select distinct user_id,sdt,IFNULL(edt,(select max(edt) from jghy)) edt from jghy) a 
left join (select distinct sdt dt from jghy
where sdt  is not null 
union 
select distinct edt dt from jghy
where edt is not null ) b  on  b.dt BETWEEN a.sdt and a.edt
left join jghy c on b.dt = c.sdt 
left join jghy d on b.dt = d.edt 
ORDER BY a.user_id,b.dt ) tmp1 
GROUP BY 2,3 ) tmp2 , (select @i:=0) rows ) tmp3 
left join (
select (@o:=@o+1) id ,tmp2.* from (
select GROUP_CONCAT(tmp1.user_id) users,tmp1.dt, tmp1.val from(
select a.user_id,b.dt,ifnull(c.sv_w,d.ev_w) val
from (select distinct user_id,sdt,IFNULL(edt,(select max(edt) from jghy)) edt from jghy) a 
left join (select distinct sdt dt from jghy
where sdt  is not null 
union 
select distinct edt dt from jghy
where edt is not null ) b  on  b.dt BETWEEN a.sdt and a.edt
left join jghy c on b.dt = c.sdt 
left join jghy d on b.dt = d.edt 
ORDER BY a.user_id,b.dt ) tmp1 
GROUP BY 2,3 ) tmp2 , (select @o:=0) rows ) tmp4 on tmp3.id = tmp4.id -1 
where tmp4.id is not null
ORDER BY tmp3.id ) tmp5
join   mysql.help_topic b on b.help_topic_id < (length(tmp5.users) - length(replace(tmp5.users,',',''))+1)  ) tmp6 
group by 1;

+---------+--------------------+
| user_id | val_u              |
+---------+--------------------+
| 1       | 60.869047619047606 |
| 2       | 50.869047619047606 |
| 3       |  37.53571428571427 |
| 4       | 44.202380952380935 |
| 5       | 42.952380952380935 |
| 6       |  22.61904761904762 |
| 7       | 40.952380952380935 |
+---------+--------------------+

-- 999 总用水量复核

select sum(val_u) from (

select tmp6.user_id,sum(tmp6.val_u) val_u from (

select substring_index(substring_index(tmp5.users,',',b.help_topic_id+1),',',-1) as user_id ,tmp5.dt,tmp5.val_u  
from  (

select tmp4.id,tmp3.users,tmp4.dt,tmp3.val,(tmp4.val -tmp3.val),(tmp4.val -tmp3.val)/LENGTH(REPLACE(tmp3.users,',','')) val_u  from (
select (@i:=@i+1) id ,tmp2.* from (
select GROUP_CONCAT(tmp1.user_id) users,tmp1.dt, tmp1.val from(
select a.user_id,b.dt,ifnull(c.sv_w,d.ev_w) val
from (select distinct user_id,sdt,IFNULL(edt,(select max(edt) from jghy)) edt from jghy) a 
left join (select distinct sdt dt from jghy
where sdt  is not null 
union 
select distinct edt dt from jghy
where edt is not null ) b  on  b.dt BETWEEN a.sdt and a.edt
left join jghy c on b.dt = c.sdt 
left join jghy d on b.dt = d.edt 
ORDER BY a.user_id,b.dt ) tmp1 
GROUP BY 2,3 ) tmp2 , (select @i:=0) rows ) tmp3 
left join (
select (@o:=@o+1) id ,tmp2.* from (
select GROUP_CONCAT(tmp1.user_id) users,tmp1.dt, tmp1.val from(
select a.user_id,b.dt,ifnull(c.sv_w,d.ev_w) val
from (select distinct user_id,sdt,IFNULL(edt,(select max(edt) from jghy)) edt from jghy) a 
left join (select distinct sdt dt from jghy
where sdt  is not null 
union 
select distinct edt dt from jghy
where edt is not null ) b  on  b.dt BETWEEN a.sdt and a.edt
left join jghy c on b.dt = c.sdt 
left join jghy d on b.dt = d.edt 
ORDER BY a.user_id,b.dt ) tmp1 
GROUP BY 2,3 ) tmp2 , (select @o:=0) rows ) tmp4 on tmp3.id = tmp4.id -1 
where tmp4.id is not null
ORDER BY tmp3.id ) tmp5
join   mysql.help_topic b on b.help_topic_id < (length(tmp5.users) - length(replace(tmp5.users,',',''))+1)  ) tmp6 
group by 1
) tmp7;

+-------------------+
| sum(val_u)        |
+-------------------+
| 299.9999999999999 |
+-------------------+


select min(sv_w),max(ev_w), max(ev_w)- min(sv_w) from jghy;
+-----------+-----------+----------------------+
| min(sv_w) | max(ev_w) | max(ev_w)- min(sv_w) |
+-----------+-----------+----------------------+
|     950.1 |    1250.1 |    299.9999999999999 |
+-----------+-----------+----------------------+

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值