关于SQL的几个面试题或练习题,在解题的时候,同时考虑用pandas来解,所以做个总结。
题目一
题目描述
用户日志表log,有用户编号cid,时间dt两个字段,查找每个用户成为新增用户的时间
HiveSQL解
思路:
- 对每个用户的出现时间进行排名
- 从排名中挑选出每个用户的排名为1的时间,即首次出现时间,也就是成为新增用户的时间
1 | select a.cid, |
Python解
现在需要新增加一列is_new
,判断当天该用户是否是新用户,若是,则对应is_new
的值设为1,否则设为0。
思路:
- 统计每个用户出现的最早时间(成为新用户时间),并用
dict(zip())
构造一个用户ID:最早时间
字典 - 将原
df_log
每个cid
的dt
与字典的dt
比较,若相同就把相应行的is_new
设为1,否则为0
具体实现:
- 构造log数据框
1 | cid=[1,2,2,3,4,3,5] |
log数据框:
- 统计每个用户成为新增用户时间,按cid分组,求最早时间
1 | minTime = df_log.groupby('cid').dt.min().tolist() |
- 统计独立用户
1 | cid = set(df_log['cid'].tolist()) |
- 构造字典:
用户: 成为新增用户的时间
1 | cid_mintime_dict = dict(zip(cid,minTime)) |
- 将df_log每行的dt与字典的dt比较,若相同,就把新增的is_new列设置为1,否则为0
1 | def match(df): |
结果:
题目二
题目描述
有两张表,t1和t2;t1有用户编号uid和页面浏览量pageviews两个字段;t2有用户编号uid和用户所在组别groupid两个字段;查询每组用户的总阅读量
两张表为:
HiveSQL 解
说明这是列转行问题,用函数
lateral view explode
拆分字段的多个值。
思路:
- 先把t2表的groupid拆分开,将得到的结果重命名
- 以新表为主,左连接t1表,对groupid分组,统计每组pageviews的和
1 | select b.groupid, |
Python 解
思路:
- 先将groupid列按属性值拆分成多行
- 合并两张表,对合并后的表按照groupid进行分组,统计pageviews的和
具体实现:
** 说明:data_t1 表示 t1 的数据框, data_t2 表示 t2 的数据框 **
- 先拆分groupid,并重命名为group
1 | newGroup = data_t2['groupid'].str.split(',', expand=True).stack().reset_index(level=1,drop=True).rename('group') |
newGroup的结果为:
- 把newGroup合并到data_t2上,得到新的data_t2
1 | data_t2 = data_t2.drop('groupid', axis=1).join(newGroup) |
结果为:
- 合并新的data_t2和data_t1,以data_t2为主
1 | df_combined = pd.merge(data_t1,data_t2,on='uid',how='right') |
结果为:
- 按照group分组,统计总浏览量
1 | df_combined.groupby('group')['pageviews'].sum() |
最终结果:
题目三
题目描述
浏览量表V,有用户编号cid,时间dt,浏览量visits字段;
选出连续3天或以上浏览量大于10的用户。
HiveSQL解
巧妙利用排序,把日期进行从小到大排序,用日期(天)本身减去日期的排名,若日期所在行的差值相同说明这些日期是连续的。
思路:
- 子查询从浏览量大于10的数据中选出用户编号cid、日期(天)与日期排名差值diff
- 外层查询选择cid,按照用户编号cid和diff分组,having过滤出计数大于等于3的
1 | select b.cid |
Python解
- 具体实现:
- 构造数据
1 | id=[1,2,3,4,5,6,7,8,9,10] |
V表数据框:
- 选出连续3天及以上visits大于等于10的那部分日期
1 | i =0 |
输出结果: