0
点赞
收藏
分享

微信扫一扫

案例分析:SQL基于MYSQL数据库的用户充值问题

Ad大成 2022-05-03 阅读 101

一、建表

1、用户注册表:结构( 1 user id 2 注册日期时间 3 性别 )

create table t_usereg(
  user_id int primary key auto_increment,--用户id主键、自增
  registration_date datetime,--注册日期时间
  sex char(1)
);

2、登录表: 结构(1 user id 2 登录日期时间 3 充值金额 4 登出日期时间 )

用户可多次重复登录,所以主键设为联合主键较为合适。

create table t_userlog(
  user_id int ,
  login_date datetime,--登录日期时间
  recharge_amount int,--充值金额
  logout_date datetime,--登出日期时间
  primary key(user_id,login_date,recharge_amount,logout_date)
);

二、模拟数据用于自测

注册表

 登录表

三、问题概览

1 今天登录人数 
2 今天和昨天都登录的 id 
3 单笔充值金额大于 500 的 id 
4 累计充值金额大于 5000 的 id 
5 累计充值金额大于 5000 注册时间大于 1 年的 id 
6 男女充值金额差值 
7 总注册用户数量和总充值用户数量 
8 近期 30 天平均在线时长 2 小时的用户数量 
9 近期 30 天没登录用户数量 
10 连续两天充值大于 100 元的 id 

四、解决问题 
1、 今天登录人数

select
  login_date,
  count( user_id)
from
  t_userlog
where
  date(login_date)=curdate();

结果输出

+---------------------+-----------------+
| login_date          | count( user_id) |
+---------------------+-----------------+
| 2022-05-03 14:24:21 |               2 |
+---------------------+-----------------+
1 row in set (0.01 sec)

思路:

  • 登录日期=系统当前日期(注意建表时登录日期类型设置的是带时分秒的,这块得转换一下)
  • curdate()函数返回的是年月日,不带时分秒

2、 今天和昨天都登录的 id 

select
  a.user_id
from (
  select
      distinct(user_id)
  from
      t_userlog 
  where curdate()-date(login_date)=1
         ) as a
join (
   select distinct(user_id)
   from
      t_userlog 
   where date(login_date)=curdate()
       ) as b
on
   a.user_id=b.user_id;

结果输出

+---------+
| user_id |
+---------+
|       2 |
|       4 |
+---------+
2 rows in set (0.01 sec)

思路:

  • 把昨天登录的用户id查出来,再把今天登录的用户查出来,然后做一个连接,此时会自动过滤掉只在其中一天登录的id。(注意id去重)
  • curdate()-date()得出来的是天数。

3 、单笔充值金额大于 500 的 id 

select
  user_id,
  recharge_amount 
from
  t_userlog 
where
  recharge_amount>500 
group by
  user_id;

结果输出

+---------+-----------------+
| user_id | recharge_amount |
+---------+-----------------+
|       1 |             600 |
|       2 |            3500 |
|       3 |             700 |
|       4 |             900 |
+---------+-----------------+
4 rows in set (0.00 sec)

思路:

  • 每个id可以重复充值,每次充值的金额不定,这就包含同一用户多次充值500以上的可能,所以要将id分组去重。此处的recharge_amount输出的是符合条件的同一个用户充值金额最大值。

4 、累计充值金额大于 5000 的 id  

select 
  user_id,
  sum(recharge_amount) as cum_amount 
from
  t_userlog 
group by
  user_id 
having
  sum(recharge_amount)>5000;

结果输出

+---------+------------+
| user_id | cum_amount |
+---------+------------+
|       2 |       6440 |
|       4 |       5500 |
+---------+------------+
2 rows in set (0.00 sec)

思路:

  • 在上一题基础上将单次金额改成累计金额,再用having筛选。(注意where后不接聚合函数)

5、 累计充值金额大于 5000 注册时间大于 1 年的 id

select
  L.user_id,
  sum(recharge_amount) as cum_amount
from
  t_userlog L
join
  t_usereg R 
on
  L.user_id=R.user_id
where
  year(curdate())-year(registration_date)>1
group by
  user_id
having
  sum(recharge_amount)>5000;

结果输出

+---------+------------+
| user_id | cum_amount |
+---------+------------+
|       2 |       6440 |
+---------+------------+
1 row in set (0.00 sec)

思路:

  • 关键词累计充值金额、注册时间,这存在于两个表,所以得在第五题基础上做个表连接,再加上where+日期函数 筛选即可。

6 、男女充值金额差值 

select
  (
   select
      sum(recharge_amount) as m_cum_amount
   from
      t_userlog L
   join
      t_usereg R 
   on
      L.user_id=R.user_id
   where
      R.sex='m'
    )-
   (
   select
      sum(recharge_amount) as f_cum_amount
    from
      t_userlog L
    join
      t_usereg R 
    on
      L.user_id=R.user_id
    where
      R.sex='f'
    ); 

结果输出

+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                                                                                                                                                                                                            -8790 |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

思路:

  • 将男用户金额查出来,女用户金额查出来,结果相减后再查询。其中充值金额和性别在两个表,注意连接。

7 、总注册用户数量和总充值用户数量

select 
  count(distinct R.user_id),
  count(distinct L.user_id)
from  
  t_userlog L
right join
  t_usereg R 
on
  L.user_id=R.user_id;

结果输出

+---------------------------+---------------------------+
| count(distinct R.user_id) | count(distinct L.user_id) |
+---------------------------+---------------------------+
|                         6 |                         5 |
+---------------------------+---------------------------+
1 row in set (0.00 sec)

思路:

  • 注册和充值在两个表得做个连接,由于注册用户可以不充值,所以总充值用户数量小于等于总注册用户数量,这个表连接得设置成以注册表为主进行连接。连接后由于笛卡尔积会生成很多条数据,但我们只需要每个id保留一条数据就好,所以将id去重后再计数。

8、 近期 30 天平均在线时长 2 小时的用户数量 

select
  count(user_id)
from
  (
   select
     user_id,
     avg(timestampdiff(hour, login_date, logout_date)) as avg_online_time--计算平均在线小时数
   from
     t_userlog 
   where
     date_sub(curdate(), interval 30 day) <= date(login_date)--筛选三十天内登录的数据
   group by
     user_id
   ) as a
where
  a.avg_online_time=2;

结果输出

+----------------+
| count(user_id) |
+----------------+
|              1 |
+----------------+
1 row in set (0.00 sec)

思路:

  • 先将用户30天内平均在线时长取出来,再筛选时长在两小时的用户
  • date_sub(date,interval expr type)函数:从日期减去指定的时间间隔
  • timestampdiff(type,date1, date2)函数:date2-date1时间间隔转化为type(可以是年、月、日、时、分、秒、季度、周)输出

9 、近期 30 天没登录用户数量 

select
  count(user_id)
from
  t_usereg
where
  user_id not in(
    select
      user_id
    from
      t_userlog 
    where
     date_sub(curdate(), interval 30 day) <= date(login_date)
                 );

结果输出

+----------------+
| count(user_id) |
+----------------+
|              1 |
+----------------+
1 row in set (0.00 sec)

思路:

  • 先查询近30天内登录过的用户,再反取即得到近30天内没登录过的用户,然后计数即可。

10、 连续两天(两天以上不算)充值大于 100 元的 id

select
  b.user_id
from 
  (
   select
     user_id,
     recharge_amount,
     login_date,
     dense_rank() over(partition by user_id order by date(login_date)) as posn--注意mysql8.0以上版本才支持窗口函数
   from
     t_userlog
  ) as b
where
recharge_amount>100
group by
  b.user_id,
  date_sub(b.login_date,interval b.posn day)
having
  count(1)=2;

思路:

同一用户连续充值时login_date-posn是相同的(在过程表1中用蓝色字体标记)

同一用户连续两天充值时login_date-posn相同的数量为2(在过程表2中用红色字体标记):

  • 按照用户id分组,每个组内按照登录时间升序排列生成序号。
表b
user_idrecharge_amountlogin_dateposn

1

5002022-04-05 12:38:241
16002022-04-29 10:38:242
18202022-05-02 15:38:243
235002022-04-24 15:39:311
224002022-05-01 15:39:312
23402022-05-02 13:24:213
22002022-05-03 14:24:214
34002022-01-25 08:14:311
37002022-04-29 09:14:312
49002022-04-29 11:40:311
415002022-05-02 11:25:542
431002022-05-03 09:23:543
51302022-04-28 07:28:291

  • 把充值金额大于100的筛选出来(巧了,所设金额全部大于100),再按用户id、(登录时间-序号天)结果分组,得到id为4的用户仅连续两天充值金额大于100
过程表1
user_idlogin_date-posnrecharge_amount组号


1
 
2022-04-04 12:38:245001
2022-04-27 10:38:246002
2022-04-29 15:38:248203

2
 
2022-04-23 15:39:3135004
2022-05-29 15:39:3124005
2022-05-29 13:24:213406
2022-05-29 14:24:212007
32022-01-24 08:14:314008
2022-04-27 09:14:317009
42022-04-28 11:40:3190010
2022-05-30 11:25:54150011
2022-05-30 09:23:54310012
52022-04-27 07:28:2913013

过程表2
user_idlogin_date-posn组号


1
 
2022-04-04 12:38:241
2022-04-27 10:38:242
2022-04-29 15:38:243

2
 
2022-04-23 15:39:314
2022-05-29 15:39:315
2022-05-29 13:24:216
2022-05-29 14:24:217
32022-01-24 08:14:318
2022-04-27 09:14:319
42022-04-28 11:40:3110
2022-05-30 11:25:5411
2022-05-30 09:23:5412
52022-04-27 07:28:2913
举报

相关推荐

0 条评论