面试题

题目:有一张用户签到表【t_user_attendence】,标记每天用户是否签到(说明:该表包含所有用户所有工作日的出勤记录) ,包含三个字段:日期【fdate】,用户id【fuser_id】,用户当天是否签到【fis_sign_in:0否1是】;

问题1:请计算截至当前每个用户已经连续签到的天数(输出表仅包含当天签到的所有用户,计算其连续签到天数)
输出表【t_user_consecutive_days】:用户id【fuser_id】,用户联系签到天数【fconsecutive_days】

用max和datediff。
思路:先找用户最近一次未签到日期,再用今天减那个日期

create table t_user_consecutive_days as 
select fuser_id
,datediff('20200322',fdate_max) fconsecutive_days
from
    (select fuser_id
    ,max(fdate) fdate_max
    from t_user_attendence
    where fis_sign_in = 0
    group by fuser_id
    ) t1
;

问题2:请计算每个用户历史以来最大的连续签到天数(输出表为用户签到表中所有出现过的用户,计算其历史最大连续签到天数)
输出表【t_user_max_days】:用户id【fuser_id】,用户最大连续签到天数【fmax_days】

思路:把用户所有签到记录转化成一条0-1字符串序列,用0做split切割,计算切出来的1序列组中的最大长度

create table t_user_max_days as
select fuser_id
,max(length(cut_fsign_record)) as fmax_days
(select fuser_id
,fsign_record
,cut_fsign_record
from
    (select fuser_id
    ,wm_concat(fis_sign_in) fsign_record
    from t_user_attendence
    group by fuser_id
    ) t1
lateral view explode(split(fsign_record,'0')) t as cut_fsign_record
) t2
where cut_fsign_record<>''
group by fuser_id
;

问题1:中的用户签到其实可以用Redis实现

使用命令:

SETBIT key offset value 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。

BITCOUNT key [start] [end] 计算给定字符串中,被设置为 1 的比特位的数量。

思路
每当用户在某一天上线的时候,我们就使用 SETBIT ,以用户名作为 key ,将那天所代表的网站的上线日作为 offset 参数,并将这个 offset 上的为设置为 1 。
如果今天是网站上线的第 100 天,而用户 peter 在今天阅览过网站,那么执行命令 SETBIT peter 100 1 ;如果明天 peter 也继续阅览网站,那么执行命令 SETBIT peter 101 1 ,以此类推。
当要计算 peter 总共以来的上线次数时,就使用 BITCOUNT 命令:执行 BITCOUNT peter ,得出的结果就是 peter 上线的总天数

性能
即使运行 10 年,占用的空间也只是每个用户 10*365 比特位(bit),也即是每个用户 456 字节。对于这种大小的数据来说, BITCOUNT 的处理速度就像 GET 和 INCR 这种 O(1) 复杂度的操作一样快。
如果你的 bitmap 数据非常大,那么可以考虑使用以下两种方法:

  • 将一个大的 bitmap 分散到不同的 key 中,作为小的 bitmap 来处理。使用 Lua 脚本可以很方便地完成这一工作。
  • 使用 BITCOUNT 的 start 和 end 参数,每次只对所需的部分位进行计算,将位的累积工作(accumulating)放到客户端进行,并且对结果进行缓存 (caching)。

问题3:统计某时间段内的活跃用户数(登陆过就算)

使用命令:

SETBIT key offset value 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。

BITOP operation destkey key [key ...]  对一个或多个保存二进制位的字符串key进行位元操作,并将结果保存到destkey上。
operation可以是AND、OR、NOT、XOR这四种操作中的任意一种。
BITOP AND destkey key [key ...]  ,对一个或多个key求逻辑并,并将结果保存到destkey
BITOP OR destkey key [key ...] ,对一个或多个key求逻辑或,并将结果保存到destkey
BITOP XOR destkey key [key ...] ,对一个或多个key求逻辑异或,并将结果保存到destkey
BITOP NOT destkey key ,对给定key求逻辑非,并将结果保存到destkey
除了NOT操作外,其他操作都可以接受一个或多个key作为输入,当BITOP处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看做0,空的key也被看作是包含0的字符串序列

思路
用时间作为key值,每天存储一个key。二进制位和用户id做映射。比如今天张三(id= 0)和李四(id = 1)都出勤了,那么

SETBIT 20200414 0 1
SETBIT 20200414 0 1
……

以此类推,最后结果使用BITOP 做或运算

bitop or destkey 20200414 20200420  (登陆过就算,所以做或运算)
BITCOUNT  destkey  0 -1    (对结果进行统计,计算人数)
原文地址:https://www.cnblogs.com/farmersun/p/12702077.html