本文同时发表于我的个人博客http://xinyuwg.com,访问该链接以获得详细信息与更好的阅读体验。
本文为原创内容,未经允许请勿转载。

至今在数据分析岗摸爬滚打已有一年,尚且不敢说自己挖掘洞见的本事提升多少。但实打实的与SQL打了一年的交道,接触过各种各样的业务场景,完成过各种千奇百怪的需求,自我感觉在sql编程上也颇有体会。

相信接触过SQL的人都明白知道其非常容易上手,作为一个结构化查询语言,其在数据提取上给人们提供了非常大的便捷。然而在考虑到开发成本和计算的复杂度的情况下,并非所有的提数需求都适合用sql来实现,也并非sql能够在各种业务场景下提供数据提取的最优解。有些时候hive streaming、spark、甚至简单的python脚本都能把sql难以实现的逻辑变得得心应手。即便如此,探究sql在一些复杂场景下的实现还是对锻炼逻辑思维很有帮助。

最近我会专门开辟一个专栏来分享这一年来我所遇到的一些比较复杂的业务场景,并力求通过纯sql的方法实现,给出我能想到的所有解法与大家分享。这也算是见证和记录自己一年来sql编程的心路历程了吧。

毕竟我的目标是data scientist呀。


背景

数据运营人员常常会需要查找活跃用户名单,而活跃用户很多情况下被定义为连续在线或发单n天及以上的用户。一方面我们可以根据n的值直接进行筛选;更具一般性地,就要求我们去求取每个用户某段时间内的最大连续在线或者发单天数了。

SQL求连续在线天数是一个非常经典的问题,该问题在不考虑计算成本下有非常多的解法。该问题也是我在面试实习生时最喜欢深入问的一个问题,在引导一个候选人去完成这个问题的过程中可以看出其对sql的理解深度以及其思维是否灵敏。

该问题的最大难点在于如何判断日期与日期间是否连续,那这就要涉及到处理行与行之间的关系了。说到这对SQL比较熟悉的同学应该就会反应出使用join或者窗口函数来处理了。


数据:

假设我们有19年一月份每日用户发单数据存储于订单表order_base:

user_id order_id create_time
234520012 1231512416323 2019-01-02 12:21:11
123149908 2412298719221 2019-01-04 01:11:34

解法1(通过与特定日期的日期差判定连续):

本方法比较tricky。连续的时间以为着这些时间点与某一个特定时间点的时间差也是连续的,从下表可以直观理解这一点:

日期 特定日期 日期差d
2019-01-01 2019-01-01 0
2019-01-02 2019-01-01 1
2019-01-04 2019-01-01 3
2019-01-05 2019-01-01 4
2019-01-06 2019-01-01 5

那么我们对该日期差d进行个排序,如果连续的话,d与序号的差值应该是相同的,如下表:

日期 特定日期 日期差d 序号r 日期差d与序号r的差值
2019-01-01 2019-01-01 0 0 0
2019-01-02 2019-01-01 1 1 0
2019-01-04 2019-01-01 3 2 1
2019-01-05 2019-01-01 4 3 1
2019-01-06 2019-01-01 5 4 1

这样答案就显而易见了,只需要对上面这个子查询的最后一列进行分组统计行数,变得到了每次连续的天数,再取连续天数的最大值,便是我们想要的答案。

selectuser_id,max(date_cnt) as max_continuation_date_cnt
from
(selectuser_id,d-d_ranking as d_group, -- 连续日期的组标记count(1) as date_cntfrom (selectuser_id,d, --与标记日期的日期差row_number() over(partition by user_id order by d) as d_ranking --与标记日期的日期差的排序from(selectuser_id,datediff(create_date,'2019-01-01') as d --与标记日期的日期差from(selectuser_id,to_date(create_time) as create_datefromorder_basegroup byuser_id,date(create_time))a -- 在这一层获取用户的发单日期并去重)b --这一层获取与标记日期的日期差)c --获取连续日期的排序group byuser_id,d-d_ranking
)d -- 获取每一个连续日期组的连续天数
group byuser_id

解法2(left join进行笛卡尔积):

假设我们不需要知道用户最大的连续天数,只需要知道某个用户是否出现连续n天(假设n为3)登录的行为。那这里首先给出一种完全不考虑计算复杂度的解法,使用纯join关联去实现该问题。

整体思路是去获得同一个用户的发单日期对,看每一个发单日期的n天内是否有n个发单日期。

selectuser_id
from
(selectuser_id,to_date(create_time) as create_datefromorder_basegroup byuser_id,date(create_time)
)a -- 在这一层获取用户的发单日期并去重
left join
(selectuser_id,to_date(create_time) as create_datefromorder_basegroup byuser_id,to_date(create_time)
)b -- 与a完全相同的逻辑,为了得到日期与日期间的关联
ona.user_id = b.user_id --仅使用user_id进行关联,获取同一个用户发单日期间的笛卡尔积
wherea.create_date <= b.create_dateand date_add(a.create_date,3) > b.create_date --以a的日期为基准,保留从a.create_date开始的3天内发单日期
group byuser_id,a.create_date
havingcount(1) = 3 --如果从a.create_date开始的3天内都有发单,则应该有3条记录

该方法容易理解,但其最大的弊端在于关联时造成的笛卡尔积大大增加了计算的复杂度。在较小的数据集上可以考虑该方法,但实际生产环境下意义并不大。


解法3 (lead或lag):

最后介绍一个最为直观,也是计算成本最小的方法。假设我们需要求连续登陆n天(假设n为7)及以上的用户,那么对于一个存在该行为的用户,他去重和排序后的发单日期信息中,必存在某一天,往前回溯(往后推)6条记录的日期,等于该日期减6(加6)。这么说可能不太好理解,但相信你看了以下代码便能很快明白我在说什么:

selectuser_id
from
(selectuser_id,create_date,lag(create_date,6,null) over(partition by user_id order by create_date) as last_6_row -- 按时间排序后6行之前的那一条记录(selectuser_id,to_date(create_time) as create_datefromorder_basegroup byuser_id,date(create_time))a -- 在这一层获取用户的发单日期并去重
)b --获取6行之前的那一条记录
wheredatediff(create_date,last_6_row) = 6
group byuser_id

总结:

如我在介绍问题背景的时候所说,处理日期间的连续性就需要将行与行之间进行关联,而sql提供的解决方案是join和窗口函数。恰恰sql的优势便在于刻画这种行数据间的关系,该问题场景能够帮助我们更深入地理解SQL的这一特性。

Hive SQL复杂场景实现(1) —— 连续发单天数相关推荐

  1. Hive_HQL_复杂SQL_连续发单天数

    原文地址: 1.Hive SQL复杂场景实现(1) -- 连续发单天数 https://blog.csdn.net/Adrian_Wang/article/details/89791948 至今在数据 ...

  2. Hive SQL— 连续发单天数

    背景 数据运营人员常常会需要查找活跃用户名单,而活跃用户很多情况下被定义为连续在线或发单n天及以上的用户.一方面我们可以根据n的值直接进行筛选:更具一般性地,就要求我们去求取每个用户某段时间内的最大连 ...

  3. SQL求用户的最大连续登陆天数

    建表插入数据 create table tmp_continous (id STRING ,time DATETIME );INSERT OVERWRITE TABLE tmp_continous S ...

  4. Hive sql 每天场景题 41

    41.现有各直播间的用户访问记录表(live_events)如下,表中每行数据表达的信息为,一个用户何时进入了一个直播间,又在何时离开了该直播间 户访问记录表(live_events)如下 user_ ...

  5. Hive sql : 查询连续登录天数

    查询连续登录天数 1.问题描述 2.在Hive中建表 3.查询最大连续登录天数 1.问题描述 目前有两列数据,分别是用户ID和用户登录的时间,现需要统计用户连续登录的最大天数,中间如有断开,则不算连续 ...

  6. SQL——最大连续登录天数、当前连续登录天数、最大连续未登录天数问题、连续登陆N天用户、连续座位号

    问题: 最大连续登录天数 当前连续登录天数 最大连续未登录天数 连续登陆3天用户(三种方法) 选出连续座位的编号 前三个问题所用数据: 原数据表:user_active表 表字段:用户.新增日期.活跃 ...

  7. Hive SQL经典面试题:统计连续登陆的三天及以上的用户

    Hive SQL经典面试题 最近发现一道大数据面试经常会问的SQL题目:统计连续登录的三天及以上的用户(或者类似的:连续3个月充值会员用户.连续N天购买商品的用户等),下面就来记录一下解题思路. 要求 ...

  8. Hive SQL间断日期补数

    Hive SQL间断日期补数 0.业务场景 场景:用户间断消费,流水中无消费无记录,对用户每天余额进行补数. 1.补充数据代码 -- 补充间断日期中的数据 with data_test as (sel ...

  9. beeline执行sql语句_由“Beeline连接HiveServer2后如何使用指定的队列(Yarn)运行Hive SQL语句”引发的一系列思考...

    背景 我们使用的HiveServer2的版本为0.13.1-cdh5.3.2,目前的任务使用Hive SQL构建,分为两种类型:手动任务(临时分析需求).调度任务(常规分析需求),两者均通过我们的We ...

最新文章

  1. PyTorch Trick集锦
  2. 平板python_Wacom平板电脑的Python示例
  3. JavaScript 知识图谱
  4. 使用手势,让键盘在点击空白处消失
  5. 【Java】switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String 上
  6. java-commons-HttpClient超时设置setConnectionTimeout和setSoTimeout
  7. Bzoj2882 工艺
  8. 实现类似shared_ptr的引用计数
  9. bulk insert
  10. 代写品牌故事-品牌故事的结构
  11. 牛客网ACM模式 JsV8和Java输入输出练习
  12. 关于安卓开发,在鸿蒙系统应用时,File读取文被拒绝访问的解决方案
  13. AI 图像识别的测试
  14. Spring的sessionFactory配置详解
  15. 读书笔记-《购物中心》
  16. 我的世界网易怎么下载java材质包_​网易我的世界材质包怎么导入
  17. 卷积神经网络基本概念
  18. java背包_java-背包的实现
  19. sql 获取日期时分秒_Sql 中获取年月日时分秒的函数
  20. 【UVA】 133 --- The Dole Queue

热门文章

  1. no input file specified解决方法
  2. notepad++ 同时搜索多个关键字
  3. Numpy删除指定行
  4. PYNQ入门(2)——启动系统及例程查看
  5. “三无”大学生,就业真难
  6. python异常-TypeError: ‘tuple‘ object is not callable.当不同的环境下同一个语句运行结果不同时,不如重启程序、更改不相关变量试试
  7. 微服务与宏服务?故事线-基本概念(理解)
  8. matlab求解LP问题
  9. 弘辽科技:淘宝悄悄公布新规,在电商赛道小步快跑。
  10. flask计算pin码