SQL是大数据从业者的必备技能,大部分的大数据技术框架也都提供了SQL的解决方案。可以说SQL是一种经久不衰、历久弥新的编程语言。尤其是在数仓领域,使用SQL更是家常便饭。本文会分享四个在面试和工作中常用的几个使用技巧,具体包括:

  • 日期与期间的使用
  • 临时表与Common Table Expression (WITH)
  • Aggregation 与CASE WHEN的结合使用
  • Window Function的其他用途

数仓?不就是写写SQL吗… [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kMDnXByk-1596778620120)(数仓面试-四个在工作后才知道的SQL密技/0.jpg)]

第一:日期与期间的使用

日期与时间段的筛选在工作中是经常被用到的,因为在拉取报表、仪表板和各种分析时,周、月、季度、年度的表现往往是分析需要考量的重点。

时间区段的提取:Extract

  • 语法
-- field可以是day、hour、minute, month, quarter等等
-- source可以是date、timestamp类型
extract(field FROM source)
  • 使用
SELECT extract(year FROM '2020-08-05 09:30:08');   -- 结果为 2020
SELECT extract(quarter FROM '2020-08-05 09:30:08');   -- 结果为 3
SELECT extract(month FROM '2020-08-05 09:30:08');   -- 结果为 8
SELECT extract(week FROM '2020-08-05 09:30:08');   -- 结果为 31,一年中的第几周
SELECT extract(day FROM '2020-08-05 09:30:08');  -- 结果为 5
SELECT extract(hour FROM '2020-08-05 09:30:08');   -- 结果为 9
SELECT extract(minute FROM '2020-08-05 09:30:08');   -- 结果为 30
SELECT extract(second FROM '2020-08-05 09:30:08');   -- 结果为 8

上面可供提取的字段,不同的数据库存在些许的差异。以Hive为例,支持day, dayofweek, hour, minute, month, quarter, second, week 和 year。其中周、月、年使用最为广泛,因为无论是公司内部产品,还是商用的产品所提供的数据后台统计,周报和月报(比如近7天、近30天)最注重表现的周期。

注意:

impala支持:YEAR, QUARTER, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND, EPOCH

Hive支持:day, dayofweek, hour, minute, month, quarter, second, week 和 year

Hive是从Hive2.2.0版本开始引入该函数

周的提取

  • 语法

在按照周的区间进行统计时,需要识别出周一的日期与周日的日期,这个时候经常会用到下面的函数:

next_day(STRING start_date, STRING day_of_week)
-- 返回当前日期对应的下一个周几对应的日期
-- 2020-08-05为周三
SELECT next_day('2020-08-05','MO') -- 下一个周一对应的日期:2020-08-10
SELECT next_day('2020-08-05','TU') -- 下一个周二对应的日期:2020-08-11
SELECT next_day('2020-08-05','WE') -- 下一个周三对应的日期:2020-08-12
SELECT next_day('2020-08-05','TH') -- 下一个周四对应的日期:2020-08-06,即为本周四
SELECT next_day('2020-08-05','FR') -- 下一个周五对应的日期:2020-08-07,即为本周五
SELECT next_day('2020-08-05','SA') -- 下一个周六对应的日期:2020-08-08,即为本周六
SELECT next_day('2020-08-05','SU') -- 下一个周日对应的日期:2020-08-09,即为本周日
-- 星期一到星期日的英文(Monday,Tuesday、Wednesday、Thursday、Friday、Saturday、Sunday)
  • 使用

那么该如何获取当前日期所在周的周一对应的日期呢?只需要先获取当前日期的下周一对应的日期,然后减去7天,即可获得:

SELECT date_add(next_day('2020-08-05','MO'),-7);

同理,获取当前日期所在周的周日对应的日期,只需要先获取当前日期的下周一对应的日期,然后减去1天,即可获得:

select date_add(next_day('2020-08-05','MO'),-1)
-- 2020-08-09

月的提取

  • 语法

至于怎么将月份从单一日期提取出来呢,LAST_DAY这个函数可以将每个月中的日期变成该月的最后一天(28号,29号,30号或31号),如下:

last_day(STRING date)
  • 使用
SELECT last_day('2020-08-05'); -- 2020-08-31

除了上面的方式,也可以使用date_format函数,比如:

SELECT date_format('2020-08-05','yyyy-MM');
-- 2020-08

日期的范围

月的Window:使用add_months加上trunc()的应用

-- 返回加减月份之后对应的日期
-- 2020-07-05
select add_months('2020-08-05', -1)-- 返回当前日期的月初日期
-- 2020-08-01
select trunc("2020-08-05",'MM')

由上面范例可见,单纯使用add_months,减N个月的用法,可以刚好取到整数月的数据,但如果加上trunc()函数,则会从前N个月的一号开始取值。

-- 选取2020-07-05到2020-08-05所有数据
BETWEEN add_months('2020-08-05', -1) AND '2020-08-05'
-- 选取2020-07-01到2020-08-05之间所有数据
BETWEEN add_months(trunc("2020-08-05",'MM'),-1) AND '2020-08-05' 

第二:临时表与Common Table Expression (WITH)

这两种方法是日常工作中经常被使用到,对于一些比较复杂的计算任务,为了避免过多的JOIN,通常会先把一些需要提取的部分数据使用临时表或是CTE的形式在主要查询区块前进行提取。

临时表的作法:

CREATE TEMPORARY TABLE table_1 AS  SELECT columnsFROM table A;
CREATE TEMPORARY table_2 AS SELECTcolumnsFROM table B;SELECTtable_1.columns,table_2.columns, c.columns
FROM table C JOIN table_1JOIN table_2;

CTE的作法:

-- 注意Hive、Impala支持这种语法,低版本的MySQL不支持(高版本支持)
WITH employee_by_title_count AS (SELECTt.name as job_title, COUNT(e.id) as amount_of_employeesFROM employees eJOIN job_titles t on e.job_title_id = t.idGROUP BY 1
),
salaries_by_title AS (SELECTname as job_title, salaryFROM job_titles
)
SELECT *
FROM employee_by_title_count eJOIN salaries_by_title s ON s.job_title = e.job_title

可以看到TEMP TABLE和CTE WITH的用法其实非常类似,目的都是为了让你的Query更加一目了然且优雅简洁。很多人习惯将所有的Query写在单一的区块里面,用过多的JOIN或SUBQUERY,导致最后逻辑丢失且自己也搞不清楚写到哪里,适时的使用TEMP TABLE和CTE作为辅助,绝对是很加分的。

第三:Aggregation 与CASE WHEN的结合使用

将Aggregation function (SUM/COUNT/COUNT DISTINCT/MIN/MAX) 结合CASE WHEN是最强大且最有趣的使用方式。这样的使用创造出一种类似EXCEL中SUMIF/COUNTIF的效果,可以用这个方式做出很多高效的分析。

  • Table Name: order
  • Column: register_date, order_date, user_id, country, order_sales, order_id

数据准备

CREATE TABLE order(register_date string,order_date string,user_id string,country string,order_sales decimal(10,2),order_id string);INSERT INTO TABLE order VALUES("2020-06-07","2020-06-09","001",'c0',210,"o1");
INSERT INTO TABLE order VALUES("2020-06-08","2020-06-09","002",'c1',220,"o2");
INSERT INTO TABLE order VALUES("2020-06-07","2020-06-10","003",'c2',230,"o3");
INSERT INTO TABLE order VALUES("2020-06-09","2020-06-10","004",'c3',200,"o4");
INSERT INTO TABLE order VALUES("2020-06-07","2020-06-20","005",'c4',300,"o5");
INSERT INTO TABLE order VALUES("2020-06-10","2020-06-23","006",'c5',400,"o6");
INSERT INTO TABLE order VALUES("2020-06-07","2020-06-19","007",'c6',600,"o7");
INSERT INTO TABLE order VALUES("2020-06-12","2020-06-18","008",'c7',700,"o8");
INSERT INTO TABLE order VALUES("2020-06-07","2020-06-09","009",'c8',100,"o9");
INSERT INTO TABLE order VALUES("2020-06-15","2020-06-18","0010",'c9',200,"o10");
INSERT INTO TABLE order VALUES("2020-06-15","2020-06-19","0011",'c10',250,"o11");
INSERT INTO TABLE order VALUES("2020-06-12","2020-06-29","0012",'c11',270,"o12");
INSERT INTO TABLE order VALUES("2020-06-16","2020-06-19","0013",'c12',230,"o13");
INSERT INTO TABLE order VALUES("2020-06-17","2020-06-20","0014",'c13',290,"o14");
INSERT INTO TABLE order VALUES("2020-06-20","2020-06-29","0015",'c14',203,"o15");

CASE WHEN 时间,进行留存率/使用率的分析

-- 允许多列去重
set hive.groupby.skewindata = false
-- 允许使用位置编号分组或排序
set hive.groupby.orderby.position.alias = trueSELECTdate_add(Next_day(register_date, 'MO'),-1) AS week_end,COUNT(DISTINCT CASE WHEN order_date BETWEEN register_date AND date_add(register_date,6) THEN user_id END) AS first_week_order,COUNT(DISTINCT CASE WHEN order_date BETWEEN date_add(register_date ,7) AND date_add(register_date,13) THEN user_id END) AS sencod_week_order,COUNT(DISTINCT CASE WHEN order_date BETWEEN date_add(register_date ,14) AND date_add(register_date,20) THEN user_id END) as third_week_order
FROM order
GROUP BY 1

上面的示例可以得知到用户在注册之后,有没有创建订单的行为。比如注册后的第一周,第二周,第三周分别有多少下单用户,这样可以分析出用户的使用情况和留存情况。

注意:上面的使用方式,需要配置两个参数:

hive.groupby.skewindata = false:允许多列去重,否则报错:

SemanticException [Error 10022]: DISTINCT on different columns not supported with skew in data

hive.groupby.orderby.position.alias = true:允许使用位置编号分组或排序,否则报错:

SemanticException [Error 10025]: line 79:13 Expression not in GROUP BY key ''MO''

CASE WHEN 时间,进行每个用户消费金额的分析

SELECTuser_id,SUM (CASE WHEN order_date BETWEEN register_date AND date_add(register_date,6) THEN order_sales END) AS first_week_amount,SUM (CASE WHEN order_date BETWEEN date_add(register_date ,7) AND date_add(register_date,13) THEN order_sales END) AS second_week_amountFROM order
GROUP BY 1

通过筛选出注册与消费的日期,并且进行消费金额统计,每个用户在每段时间段(注册后第一周、第二周…以此类推)的消费金额,可以观察用户是否有持续维持消费习惯或是消费金额变低等分析。

CASE WHEN数量,消费金额超过某一定额的数量分析

SELECTuser_id,COUNT(DISTINCT CASE WHEN order_sales >= 100 THEN order_id END) AS count_of_order_greateer_than_100
FROM order
GROUP BY 1

上面的示例就是类似countif的用法,针对每个用户,统计其订单金额大于某个值的订单数量,分析去筛选出高价值的顾客。

CASE WHEN数量,加上时间的用法

SELECTuser_id,MIN(CASE WHEN order_sales > 100 THEN order_date END) AS first_order_date_over1000,MAX(CASE WHEN order_sales > 100 THEN order_date END) AS recent_order_date_over100
FROM order
GROUP BY 1

CASE WHEN加上MIN/MAX时间,可以得出该用户在其整个使用过程中,首次购买超过一定金额的订单日期,以及最近一次购买超过一定金额的订单日期。

第四:Window Function的其他用途

Window Function既是工作中经常使用的函数,也是面试时经常被问到的问题。常见的使用场景是分组取topN。本文介绍的另外一个用法,使用开窗函数进行用户访问session分析。

session是指在指定的时间段内用户在网站上发生的一系列互动。例如,一次session可以包含多个网页浏览、事件、社交互动和电子商务交易。session就相当于一个容器,其中包含了用户在网站上执行的操作。

session具有一个过期时间,比如30分钟,即不活动状态超过 30 分钟,该session就会过时。

假设张三访问了网站,从他到达网站的那一刻开始,就开始计时。如果过了 30 分钟,而张三仍然没有进行任何形式的互动,则视为本次session结束。但是,只要张三与某个元素进行了互动(例如发生了某个事件、社交互动或打开了新网页),就会在该次互动的时间基础上再增加 30 分钟,从而重置过期时间。

数据准备

  • Table Name: user_visit_action
  • Columns: user_id, session_id , page_url, action_time
CREATE TABLE user_visit_action( user_id string,session_id string,page_url string,action_time string);INSERT INTO TABLE user_visit_action VALUES("001","ss001","http://a.com","2020-08-06 13:34:11.478");
INSERT INTO TABLE user_visit_action VALUES("001","ss001","http://b.com","2020-08-06 13:35:11.478");
INSERT INTO TABLE user_visit_action VALUES("001","ss001","http://c.com","2020-08-06 13:36:11.478");INSERT INTO TABLE user_visit_action VALUES("001","ss002","http://a.com","2020-08-06 14:30:11.478");
INSERT INTO TABLE user_visit_action VALUES("001","ss002","http://b.com","2020-08-06 14:31:11.478");
INSERT INTO TABLE user_visit_action VALUES("001","ss002","http://e.com","2020-08-06 14:33:11.478");
INSERT INTO TABLE user_visit_action VALUES("001","ss002","http://f.com","2020-08-06 14:35:11.478");INSERT INTO TABLE user_visit_action VALUES("002","ss003","http://u.com","2020-08-06 18:34:11.478");
INSERT INTO TABLE user_visit_action VALUES("002","ss003","http://k.com","2020-08-06 18:38:11.478");

用户访问session分析

范例的资料表如上,有使用者、访次和页面的连结和时间。以下则使用partition by来表达每个使用者在不同访次之间的浏览行为。

SELECTuser_id,session_id,page_url,DENSE_RANK() OVER (PARTITION BY user_id, session_id ORDER BY action_time ASC) AS page_order,MIN(action_time) OVER (PARTITION BY user_id, session_id) AS session_start_time,MAX(action_time) OVER (PARTITION BY user_id, session_id) AS session_finisht_time
FROM user_visit_action

上面的查询会返回针对每个用户、每次的到访,浏览页面行为的先后次序,以及该session开始与结束的时间,以此为基础就可以将这个结果存入TEMP TABLE或是CTE ,进行更进一步的分析。

小结

本文主要分享了四个在工作和面试中经常遇到的SQL使用技巧。当然,这些都与具体的分析业务息息相关。最后,不管你是SQL boy or SQL girl,只要是掌握一些技巧,相信都能够Happy SQL querying

数仓面试|四个在工作后才知道的SQL密技相关推荐

  1. sql order by 降序_数仓面试|四个在工作后才知道的SQL密技

    SQL是大数据从业者的必备技能,大部分的大数据技术框架也都提供了SQL的解决方案.可以说SQL是一种经久不衰.历久弥新的编程语言.尤其是在数仓领域,使用SQL更是家常便饭.本文会分享四个在面试和工作中 ...

  2. Hive SQL: 四个在工作后才知道的SQL密技

    SQL是大数据从业者的必备技能,大部分的大数据技术框架也都提供了SQL的解决方案.可以说SQL是一种经久不衰.历久弥新的编程语言.尤其是在数仓领域,使用SQL更是家常便饭.本文会分享四个在面试和工作中 ...

  3. 有哪些事情是你学了计算机后才知道的?

    随着信息时代的到来,各行各业均与互联网息息相关,计算机逐渐渗透到了各行各业中,社会对计算机类人才的需求大增.计算机行业也是凭借着优秀的薪资待遇,吸引着越来越多的人从事这一行业. 社会上对于程序员有着各 ...

  4. 装修后才知道的79件事

    1. 如果家中会做饭频率很高的话,尽量放弃开放式厨房,会有油烟问题 2. 卫生间.厨房小水宝,热水来得快,使用时比较方便 3. 吸油烟机中式的比欧式的吸力更强,欧式的好看不中用. 4. 电源插座能多装 ...

  5. 装修后才知道的79件事,无数网友真金白银砸出来...

    1. 如果家中会做饭频率很高的话,尽量放弃开放式厨房,会有油烟问题 2. 卫生间.厨房小水宝,热水来得快,使用时比较方便 3. 吸油烟机中式的比欧式的吸力更强,欧式的好看不中用. 4. 电源插座能多装 ...

  6. 有哪些道理是我当了程序员后才知道的?

    1.当你明白了技术永远是为了业务服务的时候,不再技术至上的时候,你就成长到程序员的下一个阶段了. 2.业务第一,产品第二,技术第三. 3.盈利了,是业务部门把钱搞来的,技术部门是支出部门. 4.年轻的 ...

  7. 有哪些是当了程序员后才知道的事?

    十年码农,深切体会,想加入程序员阵营的一定要记住这十点: 1 原来钱多,是拿加班换的. 这个行业加班是必然的,因为节奏加速,竞争加大,所以都是在赶进度,你不要被HR说的偶尔加班忽悠,基本上是100%加 ...

  8. 装修后才知道的79件事,无数网友真金白银砸出来的经验

    1. 如果家中会做饭频率很高的话,尽量放弃开放式厨房,会有油烟问题 2. 卫生间.厨房小水宝,热水来得快,使用时比较方便 3. 吸油烟机中式的比欧式的吸力更强,欧式的好看不中用. 4. 电源插座能多装 ...

  9. 有什么是你追了很多女生都失败后才知道的?

    1.需要追的女生,你们八成不合适,如果你们要是合适,女生一般会很配合你. 2.女人嘴里骂着渣男,身体却很喜欢.女人嘴上说着喜欢老实人,却无一例外,一个都看不上. 3.千万不要娇惯女生,否则作起来要你命 ...

最新文章

  1. fedora java 开发环境_Linux(Fedora 14)下 java开发环境配置 ——jdk的安装与配置
  2. 警告:failed to load the sqljdbc_auth.dll cause no sqljdbc_auth in java.library.path
  3. MyEclipse使用总结——MyEclipse去除网上复制下来的来代码带有的行号
  4. React 和 Vue的特点
  5. 学习ASP.NET Core Razor 编程系列十三——文件上传功能(一)
  6. Ribbon、Feign和OpenFeign的区别来了
  7. python3--匿名函数
  8. SMPL: A Skinned Multi-Person Linear Model
  9. java获取响应网页源代码
  10. 头条限流是什么原因_教训:千万不要用百家号去绑定头条号,后果你想象不到...
  11. navicat produsts注册机出现Generate First a serial
  12. R语言与抽样技术学习笔记(Randomize)
  13. linux cat 颜色,使用lolcat为您的Linux终端带来彩虹般美丽的色彩
  14. 软件是用计算机解决问题的过程,1.1 计算机解决问题的过程
  15. 如何用电脑录制一个解说视频
  16. openstack queens版本修改admin密码
  17. 在kafka中,可以这么理解topic,partition,broker
  18. (九)隐私计算--安全多方计算
  19. 如何撰写计算机SCI论文的引言部分 - 易智编译EaseEditing
  20. 原神要求特别多的作家千来神祠栖木在哪

热门文章

  1. 光伏企业“走出去”:创新合作方式 避免无序竞争
  2. 新需求、新政策:亚洲光伏新兴市场走强
  3. displacement map置换贴图
  4. r语言pls分析_科学网—R语言统计:偏最小二乘路径模型(plspm) - 涂波的博文...
  5. SSM+Redis的小demo
  6. wps js生成条形码,批量生成code128条形码小标签并导出为pdf文件
  7. 产品分析|网易云音乐-音乐社区的未来
  8. 外国高等学校名单 - 国外院校
  9. openwrt添加驱动代码的方法总结
  10. 微信小程序 如何增加《用户服务协议》及《隐私政策》