练习2

题目1:

假设云音乐数据库里面现在有几张如下简化的数据表:
关注follow表,第一列是关注人的id,第二列是被关注人的id,这2列的id组成主键

user_id

follower_id

1

2

1

4

2

3


这张表的第一行代表着用户id为1的关注着id为2的用户

这张表的第二行代表着用户id为1的关注着id为4的用户
这张表的第三行代表着用户id为2的关注着id为3的用户
个人的喜欢的音乐music_likes表,第一列是用户id,第二列是喜欢的音乐id,这2列的id组成主键

user_id

music_id

1

17

2

18

2

19

3

20

4

17


这张表的第一行代表着用户id为1的喜欢music_id为17的音乐
....
这张表的第五行代表着用户id为4的喜欢music_id为17的音乐

音乐music表,第一列是音乐id,第二列是音乐name,id是主键

id

music_name

17

yueyawang

18

kong

19

MOM

20

Sold Out


请你编写一个SQL,查询向user_id = 1 的用户,推荐其关注的人喜欢的音乐。
不要推荐该用户已经喜欢的音乐,并且按music的id升序排列。你返回的结果中不应当包含重复项
上面的查询结果如下:

music_name

kong

MOM

 

#方法一

select distinct m.music_name

from follow f join music_likes ml on ml.user_id=f.follower_id

join music m on ml.music_id=m.id

where music_id not in (

    select music_id

    from music_likes ml2

    where user_id=1

)

and f.user_id=1

order by m.id

 

#方法二

SELECT distinct m.music_name

FROM follow f, music_likes ml, music m

WHERE f.follower_id=ml.user_id and ml.music_id=m.id

and f.user_id=1

and m.id NOT IN (select music_id

                 from music_likes

                 where user_id=1)

ORDER BY m.id;

 

分析思路:

第一步:将三个表进行连接,找到关注人喜欢的音乐

第二步:筛选用户id为1

第三步:去除用户喜欢过的电影

第四步:对关注人喜欢的音乐去重(可能存在关注人同时喜欢某一首音乐)

题目2:

现在有一个需求,让你统计正常用户发送给正常用户邮件失败的概率:
有一个邮件(email)表,id为主键, type是枚举类型,枚举成员为(completed,no_completed),completed代表邮件发送是成功的,no_completed代表邮件是发送失败的。简况如下:

id

send_id

receive_id

type

date

1

2

3

4

5

6

2

1

1

3

3

4

3

3

4

1

4

1

completed

completed

no_completed

completed

completed

completed

2020-01-11

2020-01-11

2020-01-11

2020-01-12

2020-01-12

2020-01-12


第1行表示为id为2的用户在2020-01-11成功发送了一封邮件给了id为3的用户;
第3行表示为id为1的用户在2020-01-11没有成功发送一封邮件给了id为4的用户;
第6行表示为id为4的用户在2020-01-12成功发送了一封邮件给了id为1的用户;
下面是一个用户(user)表,id为主键(注意这里id代表用户编号),is_blacklist为0代表为正常用户,is_blacklist为1代表为黑名单用户,简况如下:

id

is_blacklist

1

2

3

4

0

1

0

0

第1行表示id为1的是正常用户;
第2行表示id为2的不是正常用户,是黑名单用户,如果发送大量邮件或者出现各种情况就会容易发送邮件失败的用户
...
第4行表示id为4的是正常用户

现在让你写一个sql查询,每一个日期里面,正常用户发送给正常用户邮件失败的概率是多少,结果保留到小数点后面3位(3位之后的四舍五入),并且按照日期升序排序,上面例子查询结果如下:

date

p

2020-01-11

2020-01-12

0.500

0.000


结果表示:

2020-01-11失败的概率为0.500,因为email的第1条数据,发送的用户id为2是黑名单用户,所以不计入统计,正常用户发正常用户总共2次,但是失败了1次,所以概率是0.500;

2020-01-12没有失败的情况,所以概率为0.000.
(注意: sqlite 1/2得到的不是0.5,得到的是0,只有1*1.0/2才会得到0.5,sqlite四舍五入的函数为round)

方法一:

#select e.date

# ,round(sum(case type when 'no_completed' then 1 else 0 end)/count(id),3) as p

#from email e

#where send_id in(select id from user where is_blacklist=0)

#and receive_id in(select id from user where is_blacklist=0)

#group by e.date

#order by e.date;

方法二:

select date,round(avg(case when type='completed' then 0 else 1 end ),3)

from email

where send_id in

(select send_id

from email,user

where send_id=user.id

and is_blacklist=0

 )

 and receive_id in

(select receive_id

from email,user

where receive_id=user.id

and is_blacklist=0

 )

 group by date

分析思路:

第一步:找到正常发送的用户

第二步:找到正常接收的用户

第三步:发送失败为1,成功为0,直接avg

题目3:

有一个薪水表salaries简况如下:

emp_no 

salary

from_date 

to_date

10001

88958

2002-06-22

9999-01-01

10002

72527

2001-08-02

9999-01-01

10003

43311

2001-12-01

9999-01-01


请你获取薪水第二多的员工的emp_no以及其对应的薪水salary,

若有多个员工的薪水为第二多的薪水,则将对应的员工的emp_no和salary全部输出,并按emp_no升序排序。

emp_no 

salary

10002

72527

方法一:

select t.emp_no,t.salary from

(

select emp_no,salary,dense_rank() over (order by salary desc) ranking

    from salaries) t

where t.ranking = 2

注意:不用加partition by 加了是输出每组第二,不加是输出全部的第二

方法二:

SELECT emp_no, salary

FROM salaries

WHERE salary = (SELECT salary

                FROM salaries

                WHERE to_date = '9999-01-01'

                GROUP BY salary

                ORDER BY salary DESC LIMIT 1,1 )  

错误解法:

如果有两个并列第一,两个并列第二:rank()是跳跃排序,结果是1133;dense_rank()是连续排序,结果是1122;row_number连续且唯一,结果是1234。所以dense_rank()最合适。

经典错误一:row_number开窗函数(当薪水第二的人有多个时只会输出一个)

1

2

3

4

5

6

select emp_no,salary

from

(select emp_no,salary,row_number() over(order by salary desc) t

from salaries

where to_date='9999-01-01')

where t=2

经典错误二:rank开窗函数(当薪水为1133情况时,输出为空)

1

2

3

4

5

6

select emp_no,salary

from

(select emp_no,salary,rank() over(order by salary desc) t

from salaries

where to_date='9999-01-01')

where t=2

题目四:

用户行为日志表tb_user_log

id

uid

artical_id

in_time

out_time

sign_cin

1

101

9001

2021-10-31 10:00:00

2021-10-31 10:00:09

0

2

102

9001

2021-10-31 10:00:00

2021-10-31 10:00:09

0

3

101

0

2021-11-01 10:00:00

2021-11-01 10:00:42

1

4

102

9001

2021-11-01 10:00:00

2021-11-01 10:00:09

0

5

108

9001

2021-11-01 10:00:01

2021-11-01 10:00:50

0

6

108

9001

2021-11-02 10:00:01

2021-11-02 10:00:50

0

7

104

9001

2021-11-02 10:00:28

2021-11-02 10:00:50

0

8

106

9001

2021-11-02 10:00:28

2021-11-02 10:00:50

0

9

108

9001

2021-11-03 10:00:01

2021-11-03 10:00:50

0

10

109

9002

2021-11-03 11:00:55

2021-11-03 11:00:59

0

11

104

9003

2021-11-03 11:00:45

2021-11-03 11:00:55

0

12

105

9003

2021-11-03 11:00:53

2021-11-03 11:00:59

0

13

106

9003

2021-11-03 11:00:45

2021-11-03 11:00:55

0

(uid-用户ID, artical_id-文章ID, in_time-进入时间, out_time-离开时间, sign_in-是否签到)

问题:统计每天的日活数及新用户占比

  • 新用户占比=当天的新用户数÷当天活跃用户数(日活数)。
  • 如果in_time-进入时间out_time-离开时间跨天了,在两天里都记为该用户活跃过。
  • 新用户占比保留2位小数,结果按日期升序排序。

方法一:

select 日期,

    count(distinct uid),

    round(sum(case when ranking=1 then 1 else 0 end)/count(distinct uid),2)

from

(select uid,

        date_format(time,'%Y-%m-%d') as '日期',

        dense_rank() over(partition by uid order by time) as ranking

    from

    (

    select uid,in_time as time from tb_user_log a

    union

    select uid,out_time as time from tb_user_log b

    ) t1

) t2

group by 日期

分析思路:

第一步:in_time-进入时间和out_time-离开时间跨天了,在两天里都记为该用户活跃过。所以需要把in_time和out_time 当成共同的时间来分析,用union

第二步:新增用户的窗口函数解法,对用户进行日期排序,排名为1对应的日期就是最早登陆日期

第三步:计算去重的用户数就是日活数量,当天排名为1的用户数就是新增用户数

第四步:日期分组,小数点位数,日期排序等等。

方法二:同样的道理

select 日期,

    count(distinct uid),

    round(sum(case when 最早登陆=日期 then 1 else 0 end)/count(distinct uid),2)

from

(select uid,日期,

       min(time) over(partition by uid ) as '最早登陆'

    from

    (

    select uid,date_format(in_time,'%Y-%m-%d') as '日期' from tb_user_log a

    union

    select uid,date_format(out_time,'%Y-%m-%d') as '日期' from tb_user_log b

    ) t1

) t2

group by 日期

方法三:左连接的方法但是当数据量大的时候可能效率低

select dt, count(t1.uid) dau, round(sum(if(new_time=dt,1,0))/count(t1.uid),2)

from

   (select uid, date(in_time) dt from tb_user_log

  union

  select uid,date(out_time) dt from tb_user_log

  ) t1

join

  (

  select uid,min(date(in_time)) new_time from tb_user_log

  group by uid

  ) t2

on t1.uid = t2.uid

group by dt

order by dt

 

posted @ 2022-09-13 19:27  萧六弟  阅读(67)  评论(0)    收藏  举报