SQL面试题挑战05:找出恶意购买用户

SQL面试题挑战05:找出恶意购买用户

问题

下面是某电商网站的订单数据,包括order_id,user_id,order_status和operate_time四个字段,我们需要找出所有恶意购买的用户。恶意购买的用户定义是:同一个用户,在任意半小时内(含),取消订单次数>=3次的就被视为恶意买家。比如该样例数据中c用户就是恶意买家。

order_id    user_id    order_status     operate_time
1101         a         已支付        2023-01-01 10:00:00
1102         a         已取消        2023-01-01 10:10:00
1103         a         待支付        2023-01-01 10:20:00
1104         b         已取消        2023-01-01 10:30:00
1105         a         待确认        2023-01-01 10:50:00
1106         a         已取消        2023-01-01 11:00:00
1107         b         已取消        2023-01-01 11:40:00
1108         b         已取消        2023-01-01 11:50:00
1109         b         已支付        2023-01-01 12:00:00
1110         b         已取消        2023-01-01 12:11:00
1111         c         已取消        2023-01-01 12:20:00
1112         c         已取消        2023-01-01 12:30:00
1113         c         已取消        2023-01-01 12:55:00
1114         c         已取消        2023-01-01 13:00:00

建表语句

drop table if exists order_info;
create table order_info(
  order_id varchar(20),
  user_id varchar(20),
  order_status varchar(10),
  operate_time datetime
);
insert into order_info values
(1101,'a','已支付', '2023-01-01 10:00:00'),
(1102,'a','已取消', '2023-01-01 10:10:00'),
(1103,'a','待支付', '2023-01-01 10:20:00'),
(1104,'b','已取消', '2023-01-01 10:30:00'),
(1105,'a','待确认', '2023-01-01 10:50:00'),
(1106,'a','已取消', '2023-01-01 11:00:00'),
(1107,'b','已取消', '2023-01-01 11:40:00'),
(1108,'b','已取消', '2023-01-01 11:50:00'),
(1109,'b','已支付', '2023-01-01 12:00:00'),
(1110,'b','已取消', '2023-01-01 12:11:00'),
(1111,'c','已取消', '2023-01-01 12:20:00'),
(1112,'c','已取消', '2023-01-01 12:30:00'),
(1113,'c','已取消', '2023-01-01 12:55:00'),
(1114,'c','已取消', '2023-01-01 13:00:00'

解答

解法1:自连接

思路:首先提取出'已取消'的订单,然后对其进行自连接,连接的条件就是id相同且操作时间相差在1-30分钟之间(这里用1作为下界可以排除很多自连接的情况)。然后按照id和operate_time分组,出现次数大于等于2次即是满足条件的用户(因为前面排除了自连接,这样就会少一次记录,所以这里大于等于2即可)。

with tmp as (
    select
        user_id,
        operate_time
    from
        order_info
    where
        order_status='已取消'
)
select
    distinct t1.user_id
from
    tmp t1
    inner join tmp t2 on t1.user_id=t2.user_id and timestampdiff(minute,t1.operate_time,t2.operate_time) between 1 and 30
group by
    t1.user_id,
    t1.operate_time
having
    count(*)>=2 -- 因为自连接没有包括自己所以这里是2

解法2:滑动窗口

思路:首先提取出'已取消'的订单,同时将datetime时间转为unix时间戳,方便后续开窗。然后对其开窗,窗口大小是operate_time和当前行operate_time差值在1800s内的行(即是以当前行为锚点,在[operate_time-1800,operate_time]的时间范围内的行),然后统计窗口内的行数即可。

with tmp as (
    select
        user_id,
        unix_timestamp(operate_time)operate_time
    from
        order_info
    where
        order_status='已取消'
)

select
    user_id
from
    (
        select
            user_id,
            count(*)over(
                partition by user_id 
                order by operate_time 
                range between 1800 preceding and current row
                )cnt
        from
            tmp
     )t0
group by
    user_id
having
    max(cnt)>=3
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值