首页 > 试题广场 >

牛客每个人最近的登录日期(三)

[编程题]牛客每个人最近的登录日期(三)
  • 热度指数:156592 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
牛客每天有很多人登录,请你统计一下牛客新登录用户的次日成功的留存率,
有一个登录(login)记录表,简况如下:
id
user_id client_id
date
1 2 1 2020-10-12
2 3 2 2020-10-12
3 1 2 2020-10-12
4 2 2 2020-10-13
5 4 1 2020-10-13
6 1 2 2020-10-13
7 1 2 2020-10-14
第1行表示user_id为2的用户在2020-10-12使用了客户端id为1的设备第一次新登录了牛客网
。。。
第4行表示user_id为2的用户在2020-10-13使用了客户端id为2的设备登录了牛客网
。。。
最后1行表示user_id为1的用户在2020-10-14使用了客户端id为2的设备登录了牛客网


请你写出一个sql语句查询新登录用户次日成功的留存率,即第1天登陆之后,第2天再次登陆的概率,保存小数点后面3位(3位之后的四舍五入),上面的例子查询结果如下:
p
0.500
查询结果表明:
user_id为1的用户在2020-10-12第一次新登录了,在2020-10-13又登录了,算是成功的留存
user_id为2的用户在2020-10-12第一次新登录了,在2020-10-13又登录了,算是成功的留存
user_id为3的用户在2020-10-12第一次新登录了,在2020-10-13没登录了,算是失败的留存
user_id为4的用户在2020-10-13第一次新登录了,在2020-10-14没登录了,算是失败的留存
故次日成功的留存率为 2/4=0.5
(sqlite里查找某一天的后一天的用法是:date(yyyy-mm-dd, '+1 day'),四舍五入的函数为round,sqlite 1/2得到的不是0.5,得到的是0,只有1*1.0/2才会得到0.5
mysql里查找某一天的后一天的用法是:DATE_ADD(yyyy-mm-dd,INTERVAL 1 DAY),四舍五入的函数为round)

示例1

输入

drop table if exists login;
CREATE TABLE `login` (
`id` int(4) NOT NULL,
`user_id` int(4) NOT NULL,
`client_id` int(4) NOT NULL,
`date` date NOT NULL,
PRIMARY KEY (`id`));

INSERT INTO login VALUES
(1,2,1,'2020-10-12'),
(2,3,2,'2020-10-12'),
(3,1,2,'2020-10-12'),
(4,2,2,'2020-10-13'),
(5,4,1,'2020-10-13'),
(6,1,2,'2020-10-13'),
(7,1,2,'2020-10-14');

输出

0.500
with t1 as (
    select user_id
          ,date
          ,datediff(date,min(date) over(partition by user_id)) as diff
    from login
)
select round(sum(t1.diff)/count(distinct a.user_id),3) as p
from login a
left join t1 on t1.user_id=a.user_id and t1.date=a.date and t1.diff=1;
可以取min开窗相减之后天数为1的那两条,再跟原表关联,两个字段相除就可以。
发表于 2025-11-11 09:15:37 回复(0)
with t1 as (select user_id,min(date) as dt
from login 
group by 1),t2 as (
select user_id,case when exists(select 1 from login where user_id=t1.user_id and datediff(date,dt)=1) then 1 else 0 end as tag
from t1 
)
select round(sum(tag)/count(user_id),3) as p
from t2 

发表于 2025-10-15 18:21:05 回复(0)
select round(sum(case when date2=date_add(date1,interval 1 day) then 1 else 0 end)/count(distinct user_id),3) p
from
(select new.user_id,date_min date1,login.date date2 from 
(select user_id,min(date) date_min from login
group by user_id) new
left join login using(user_id)) new22

发表于 2025-08-22 15:25:21 回复(0)
select round(sum(if(date + 1 = lead_date, 1, 0))/count(DISTINCT user_id), 3) p
from (select user_id, date, lead(date) over(partition by user_id) lead_date
from login) a
where (user_id, date) in (
    select user_id, min(date) from login
    group by user_id
)
这样没毛病。计算留存率,第一个想到lead()
发表于 2025-05-29 17:33:49 回复(0)
注意查找所有用户的sql写法
select count(distinct user_id) from login
select round(count(*)/(select count(distinct user_id) from login), 3) p
from login
where (user_id, date) in (select user_id, date_add(min(date),interval 1 day) from login group by user_id)


发表于 2025-03-02 10:40:54 回复(0)
select
    count(*) num1
from
    login
group by
    user_id
select
    count(*) num2
from
    login
group by
    user_id
having
    (date_add (date, interval 1 day) in date)
select
    round(1.0 * num2 / num1, 3) p
from
    login
哪里错了呀
发表于 2025-01-31 20:43:22 回复(0)
发表于 2025-01-08 10:22:03 回复(0)
select round(count(distinct l2.user_id)/count(distinct l1.user_id),3)
from login l1
left join login l2 on l1.date = date_add(l2.date,interval 1 day) and l1.user_id = l2.user_id
order by l1.user_id
g搞不明白为什么连续三天登录,不能算两次非要算一次,那如果连续两天然后间断一天又连续两天的话这个代码就明显不行了
发表于 2025-01-05 14:49:52 回复(1)
窗口lead函数  
select round(sum(case when date_add(date,INTERVAL 1 DAY)=date_1 then 1 else 0 end)/count(user_id),3) p
from 
(select user_id
       ,date
       ,row_number()over(partition by user_id order by date)r
       ,lead(date,1)over(partition by user_id order by date) date_1
       from login
)a
where r=1

发表于 2024-12-25 15:04:54 回复(1)
这个为什么不对啊
select round(count(distinct t2.user_id,t2.date)/count(distinct t1.user_id,t1.date),3) p
from login t1
left join(select user_id, date_add(date,interval 1 day) date
from login) t2 on t1.user_id=t2.user_id and t1.date=t2.date
发表于 2024-08-23 23:18:35 回复(0)
这是什么隐藏bug啊!求
有没有大神指点一下,一样的代码,为什么代码DG跑的结果行数居然复制到牛客网页跑的结果行数会不一样!这是什么隐藏bug啊!
  ifnull(lead(date,1) over(partition by user_id order by date),0) as secdate”这窗口结合“distinct user_id” 为什么会出现8行呢?!!!!应该只有4条结果才对啊!!  求高手稍微指点一下!!
select round(count(if(datediff(secdate,firdate)=1,1,null))/count(user_id),3) as p
from (
select  distinct user_id ,     min(date) over(partition by user_id) as firdate ,
         ifnull(lead(date,1) over(partition by user_id order by date),0) as secdate
from login) as sl;
发表于 2024-07-10 13:01:44 回复(0)
select round(count(if(datediff(max_date,min_date)=1,user_id,null))/count(user_id),3) from (select user_id,max(date) max_date,min(date) min_date from (select user_id,date,rank() over(partition by user_id order by date) pm from login)t1
where pm<=2
group by user_id)t2;
发表于 2024-06-28 16:22:08 回复(0)

学习评论区的。

-- 新登录用户的次日成功的留存率
select
    round(count(distinct user_id)/(select count(distinct user_id) from login),3)
from login
where (user_id,date) in (
    select user_id,date_add(min(date),interval 1 day) from login group by user_id
)
发表于 2024-06-10 21:14:26 回复(0)
select round(sum(if(datediff(l.date,sd)=1,1,0))/sum(if(l.date=sd,1,0)),3) as p
from login l
join(
select l.user_id,min(date) as sd
from login l
group by user_id) t on t.user_id=l.user_id 

发表于 2024-05-28 15:11:35 回复(0)
-- 提供一个基于exists的筛出符合条件的user_id的写法(通过自关联来锁定user_id和date)
select
round(
count(distinct (user_id)) / (
select
count(distinct (user_id))
from
login
),
3
) as p
from
login l1
where
exists (
select
*
from
login l2
where
l2.user_id = l1.user_id
and l2.date = DATE_ADD(l1.date, INTERVAL 1 DAY)
);

发表于 2024-05-20 16:06:14 回复(0)