首页 > 试题广场 >

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

[编程题]牛客每个人最近的登录日期(三)
  • 热度指数:148305 时间限制: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
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 回复(0)
这个为什么不对啊
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)
select round(count(l1.user_id)/count(l.user_id),3) p
from (select user_id,min(date) date from login group by user_id) l
left join (select distinct user_id,date from login) l1
on l.user_id=l1.user_id and l1.date=date_add(l.date,interval 1 day)
发表于 2024-04-28 00:08:46 回复(0)
(借鉴大牛的)
整合思路:
1、次日成功登录,意味着同一个user_id在min(date)次日也登录了;
2、需要满足条件。 即user_id, date存在于检索后的表格中;
3、检索表格即查询user_id, DATE_ADD(min(date), INTERVAL 1 DAY);
4、满足条件后计算的user_id的数量(count(user_id))即为次日也登录成功的用户;
5、满足条件的用户数/总用户数即得出比率;
6、最后用ROUND函数精确到三位小数。

具体代码如下:
SELECT 
    ROUND(count(user_id)/(SELECT count(DISTINCT user_id) FROM login), 3) AS p
FROM login 
WHERE (user_id, date) IN(
    SELECT 
        user_id,
        DATE_ADD(min(date), INTERVAL 1 DAY)
    FROM login
    GROUP BY user_id
);


发表于 2024-04-11 12:34:31 回复(0)
select
round(count(lo.user_id)/count(mi.user_id),3) as p
from 
    ( select 
            user_id
            ,min(date) as md 
            from login 
            group by 1
    ) mi 
left join login lo 
on mi.user_id=lo.user_id and date=date_add(md,interval 1 day)

编辑于 2024-04-08 16:12:15 回复(0)