一、建表
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分组,每个组内按照登录时间升序排列生成序号。
user_id | recharge_amount | login_date | posn |
1 | 500 | 2022-04-05 12:38:24 | 1 |
1 | 600 | 2022-04-29 10:38:24 | 2 |
1 | 820 | 2022-05-02 15:38:24 | 3 |
2 | 3500 | 2022-04-24 15:39:31 | 1 |
2 | 2400 | 2022-05-01 15:39:31 | 2 |
2 | 340 | 2022-05-02 13:24:21 | 3 |
2 | 200 | 2022-05-03 14:24:21 | 4 |
3 | 400 | 2022-01-25 08:14:31 | 1 |
3 | 700 | 2022-04-29 09:14:31 | 2 |
4 | 900 | 2022-04-29 11:40:31 | 1 |
4 | 1500 | 2022-05-02 11:25:54 | 2 |
4 | 3100 | 2022-05-03 09:23:54 | 3 |
5 | 130 | 2022-04-28 07:28:29 | 1 |
- 把充值金额大于100的筛选出来(巧了,所设金额全部大于100),再按用户id、(登录时间-序号天)结果分组,得到id为4的用户仅连续两天充值金额大于100
user_id | login_date-posn | recharge_amount | 组号 |
1 | 2022-04-04 12:38:24 | 500 | 1 |
2022-04-27 10:38:24 | 600 | 2 | |
2022-04-29 15:38:24 | 820 | 3 | |
2 | 2022-04-23 15:39:31 | 3500 | 4 |
2022-05-29 15:39:31 | 2400 | 5 | |
2022-05-29 13:24:21 | 340 | 6 | |
2022-05-29 14:24:21 | 200 | 7 | |
3 | 2022-01-24 08:14:31 | 400 | 8 |
2022-04-27 09:14:31 | 700 | 9 | |
4 | 2022-04-28 11:40:31 | 900 | 10 |
2022-05-30 11:25:54 | 1500 | 11 | |
2022-05-30 09:23:54 | 3100 | 12 | |
5 | 2022-04-27 07:28:29 | 130 | 13 |
user_id | login_date-posn | 组号 |
1 | 2022-04-04 12:38:24 | 1 |
2022-04-27 10:38:24 | 2 | |
2022-04-29 15:38:24 | 3 | |
2 | 2022-04-23 15:39:31 | 4 |
2022-05-29 15:39:31 | 5 | |
2022-05-29 13:24:21 | 6 | |
2022-05-29 14:24:21 | 7 | |
3 | 2022-01-24 08:14:31 | 8 |
2022-04-27 09:14:31 | 9 | |
4 | 2022-04-28 11:40:31 | 10 |
2022-05-30 11:25:54 | 11 | |
2022-05-30 09:23:54 | 12 | |
5 | 2022-04-27 07:28:29 | 13 |