01、写在前面
SQL作为数据分析师必备技能之一,无论是初级分析师还是高级分析师,SQL已经是各大公司招聘条件里的必选项,为什么SQL对于数据分析师来说如此重要呢?在回答这个问题之前,我们先搞懂以下几个问题。
第一个问题,SQL是啥?
SQL是Structured Query Language的缩写,意思是结构化查询语言,是一种在数据库管理系统(RelationalDatabase Management System, RDBMS)中查询数据,或通过RDBMS对数据库中的数据进行更改的语言。
看不懂。。。能不能说人话?好嘞,SQL就是一种对数据库中的数据表或者数据进行增、删、改、查等操作的语言。
什么是数据库?“数据库是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。说人话就是按照一定的组织结构存储数据的仓库。我们常见的Oracle,MySQL,SQL Server都是数据库,只是有一些是商业的数据库,一些是开源免费的而已。
第二个问题,数据分析为啥要学SQL?
其实理解了数据库和SQL的关系之后,这个问题就是句废话了。巧妇难为无米之炊,数据分析的第一步肯定是要有数据,数据哪里来?肯定要从数据库中取出来,SQL就是这样一个方便、普适的取数工具,因为几乎所有的数据库的SQL语法都是相似的,甚至现在我们做大数据分析用到的Hive SQL,其语法99%也是和SQL一样的,所以学会SQL基本上就掌握了所有数据库的取数方法。
第三个问题,到底怎么学习SQL?
你以为SQL只是一个简单的取数工具,把数据从数据库中拉出来就完事了?
1、SQL不仅能取数据,还提供了丰富的函数,可以做数据的清洗、转换等数据处理,而且SQL还能像Excel的透视表那样,可以方便地在不同的维度上对数据进行求和、计数、去重计数、求平均等操作,进而对数据进行分析,而这一切,也只需要短短的几行SQL代码就能实现。
2、如果数据很多很复杂,像Excel那样存放在不同的Sheet里,要汇总在一起进行分析,要怎么处理呢?SQL的强大之处就在于可以非常方便地将不同的数据按照一定的关联连接起来,这个关联可以是内连接inner join (找两个表的交集)、左连接left join (交集并且左表所有)、右连接(right join 交集并且右表所有)、全连接outer join(找两个表的并集),可以通过各种不同的关联条件可以实现各种不同的数据连接,最终对连接后的数据进行分析。
3、通过以上两点来看,SQL好像和Excel功能上没啥区别啊?Excel也能做数据清洗,透视表也能做求和、计数等聚合操作,Excel的Power Pivot也能实现多个表之间的连接。实际上,SQL除了以上这些功能之外,还提供了一个非常强大的功能:窗口函数,窗口函数有什么用呢?如果我们要计算每个人在特定分组下的排名、每月销售额的同比、环比、截至每天的累计销售额,这些数据分析中经常遇到的、且基础的SQL语句无法很好解决的问题,窗口函数就显示出它的威力了。所以窗口函数也是判断你是SQL基础玩家和高阶玩家的重要标准,也是数据分析面试中最喜欢考查的内容之一。
那么想入行数据分析的同学来说,怎么快速高效地掌握SQL这个数据分析的利器呢?根据前面的介绍,提升SQL水平可以按照这样的学习路径:
1、SQL基础语法:首先熟悉SQL的基础语法,对于数据分析而言,重点掌握数据查询SELECT,包括:如何使用WHERE进行数据筛选,熟练使用算数运算符(+-*/)、逻辑运算符(AND /OR/NOT)进行字段计算和条件过滤,使用SUM 、COUNT、AVG等聚合函数结合GROUP BY进行不同维度下的汇总分析,如何用HAVING子句对聚合的结果进行过滤,并使用ORDER BY 对最终的查询结果进行排序。这一部分最最重要的一点是:要明确SQL语句的执行顺序与书写书序的差异,这一点对于了解SQL的执行过程很有帮助。作为SQL系列文章的第一篇内容,我们会在本文中重点讲解。
2、SQL常用函数:在掌握了SQL基础语法的基础上,下面就要熟练掌握一些数据分析中常用的函数,包括但不限于:日期类函数、字符串类函数,数值运算类函数等。熟练使用这些函数可以帮助我们高效地做数据的清洗、转换等数据处理工作。这部分内容我们会在SQL系列文章的第二篇重点讲解。
3、SQL子查询与表连接:在之前的基础上,我们还要熟练掌握子查询和多个表之间的连接。数据分析工作中,往往需要对多张有关联的表进行分析,对于简单的查询,我们可以使用嵌套的子查询解决。但如果涉及的表很多,且表之间的关联关系比较复杂,我们就需要使用表连接,按照一定的关联关系将各个表连接在一起,常见的连接类型有内连接:INNER JOIN 、左连接:LEFT JOIN、右连接:RIGHT JOIN 、全连接:FULL JOIN。这部分内容我们会在SQL系列文章的第三篇文章中重点讲解。
4、SQL窗口函数:前面三部分基本上已经涵盖了数据分析对于SQL的基础要求,但是实际工作中,我们有一些比较复杂且常见的场景,使用基础语法并不能很好的解决,比如:每月销售额的同比/环比、截至每天的累计销售额、每种商品在它所属分类下的销售额排名,窗口函数就是为这些场景而生的,如果能熟练掌握窗口函数,数据分析中就基本上不会遇到什么SQL的问题了。这部分内容作为SQL系列文章的压轴,会在第四篇文章中重点讲解。
另外,前面已经总结了一些数据分析中常用的Excel使用技巧,有兴趣的可以翻看之前的文章,包括:
【数据分析工具】数据分析案例实操,手把手教你学PowerBI !
【数据分析工具】Excel也能玩转大数据分析?是时候祭出超级透视表Power Pivot了!
【数据分析工具】数据透视表:菜鸟也能做数据分析(文末送教程)
【数据分析工具】数据分析必知必会的Excel函数(文末领取Excel教程)
02、SQL基础语法
1、SQL基础操作
前面已经按照从基础到高阶逐步提升的角度对SQL的学习阶段进行了划分,下面我们就来讲讲学习SQL的第一阶段:SQL基础语法。
SQL是一种对数据库中的数据表或者数据进行增、删、改、查等操作的语言。根据操作对象的不同,我们把SQL的基础操作分为以下几类:
DDL(Data Definition Language,数据定义语言)
用来创建、删除或者修改数据库以及数据库中的数据表等对象。DDL 包含以下几种指令。
CREATE:创建数据库、数据表等对象
DROP:删除数据库、数据表等对象
ALTER:修改数据库、数据表等对象的结构
DML(Data Manipulation Language,数据操纵语言)
用来查询、新增、修改或者删除数据表中的记录。DML 包含以下几种指令。
SELECT:查询数据表中的数据
INSERT:向数据表中插入新数据
UPDATE:修改数据表中的数据
DELETE:删除数据表中的数据
DCL(Data Control Language,数据控制语言)
用来确认或者取消对数据库中的数据进行的变更。除此之外,还可以对数据库用户的权限进行设定。DCL 包含以下几种指令。
COMMIT:确认对数据库中的数据进行的变更
ROLLBACK:取消对数据库中的数据进行的变更
GRANT:赋予用户操作权限
REVOKE:取消用户的操作权限
作为数据分析师,我们的工作重心在于提取已有数据,分析数据背后的业务价值,所以绝大多数时候我们只需要用到数据查询SELECT,而不需要或者也不允许对数据库、数据表进行增、删、改等操作,毕竟公司也怕你删库跑路,哈哈哈!所以我们文章也会着重讲解如何使用SQL进行高效的数据提取和分析。
下面我们就通过一个实际案例,手动创建数据库和数据表,手动插入一些数据,然后基于这些数据完整地从头到尾讲解一下SQL的基础语法,希望想学习SQL的小伙伴也动起手来,一边学习一边操作,在实践中学习才是最高效的学习方法。我们这里使用的数据库是MySQL,使用的数据库工具是MySQL Workbench 8.0 CE,具体的数据库和工具我们到MySQL官网下载后安装就可以了,我们就不再展开了。
2、创建数据库
为了方便我们进行后面的演示,我们首先需要建立一个数据库,用于存放我们需要的数据表,打开MySQL Workbench 8.0 CE如下图所示,主要功能介绍如下:
在中间空白处输入SQL代码:
-- 说明:本数据仅为演示需要,无需关注数据真实性
-- 1、 创建数据库Sales,用于存放演示的数据
CREATE DATABASE Sales;
注意代码中的—-是注释符,意味着后面的内容仅为更好地理解下面的代码而做的说明,并不会执行。点击图中的执行按钮,执行代码,执行成功后,会在查询结果区显示,并可以在数据库预览区发现多了一个数据库Sales。
3、创建数据表(CREATE TABLE)
我们在Sales数据库下创建一个产品销售明细表Product,用于记录每天各个商品的销售情况,其中包括以下字段:
USE Sales; -- 转到Sales数据库下执行以下操作
CREATE TABLE Product
(
product_id CHAR(4) NOT NULL,-- 产品id,字符类型CHAR
product_name VARCHAR(100) NOT NULL,-- 产品名称,字符类型VARCHAR
product_category VARCHAR(32) NOT NULL,-- 产品所属类别,字符类型VARCHAR
sale_price INT,-- 产品售价,整数类型INT
cost_price INT,-- 产品成本价,整数类型INT
sale_date DATE -- 销售日期,日期类型DATE
)
4、向表中插入数据(INSERT)
用于存放各产品销售记录的product表已经创建好,下面我们就可以向表中插入数据,这些数据只是用来演示说明,并无实际意义和真实性。
INSERT INTO Product VALUES ('0001', 'iPHONE', '手机', 8000, 6500, '2020-09-20');
INSERT INTO Product VALUES ('0002', 'MacBook Pro', '电脑', 9500, 8000, '2020-09-11');
INSERT INTO Product VALUES ('0003', 'HUAWEI Mate40 Pro', '手机', 6000, 4800, '2020-09-11');
INSERT INTO Product VALUES ('0004', '索尼电视机', '电视机', 9000, 6800, '2020-09-20');
INSERT INTO Product VALUES ('0005', 'TCL电视机', '电视机', 6800, 5000, '2020-01-15');
INSERT INTO Product VALUES ('0006', '创维电视机', '电视机', 5000, 3000, '2020-09-20');
INSERT INTO Product VALUES ('0007', '小米电视机', '电视机', 3800, 2500, '2008-04-28');
INSERT INTO Product VALUES ('0008', '联想笔记本', '电脑', 4000, 3000,'2020-11-11');
插入成功后,查询结果区会有成功的标志。
5、查询数据(SELECT)
使用SELECT子句可以从表中查询出需要的列。
1、为了方便我们理解各列是什么含义,我们可以通过AS为列设定别名;
2、我们可以新增一列常量列constant,即这一列的所以值都是我们指定的常量;
3、如果我们希望查询结果按照某个字段进行排序,可以使用ORDER BY DESC/ASC进行降序/升序排序;
4、另外,为了防止我们查询的数据量太大,我们可以使用limit 5,把查询结果限制在5行。
例如:我们想看看按照产品id升序排列的5条记录。
SELECT
product_id AS '产品id',
product_name AS '产品名称',
cost_price AS '成本价格',
'我是个常量' AS constant
FROM Product
ORDER BY product_id ASC
limit 5;
结果如下所示:
6、指定查询的条件(WHERE)
很多时候我们只需要选取部分数据,而不是整个数据表的数据,所以这个时候我们需要在查询的时候设置过滤条件,筛选出我们需要的数据。
例如:我们要查看Product中所有“手机”类产品的产品名称。
SELECT DISTINCT product_name, product_category
FROM Product;
WHERE product_category = '手机';
结果如下:
7、算术运算符和比较运算符
有时候我们需要对数据表中的字段进行加减乘除(+-*/)运算以产生一个新的字段,比如我们如果想要计算每笔交易中的净利润,就可以通过销售价sale_price减去成本价格cost_price获得。
除了算数运算符(+-*/),我们在需要设置筛选条件查询数据的时候,常常需要用到如下所示的比较运算符,做一些复杂的过滤条件。
等于 =
不等于 <>
大于 >
大于等于 >=
小于 << span="">
小于等于 <=< span="">
介于两者之间 BETWEEN
我们通过一个例子,将算数运算符和比较运算符结合使用。
例如:我们想查看利润不低于500的所有的销售记录。
SELECT
product_id,
product_name,
sale_price,
cost_price,
sale_price-cost_price AS 'profit' -- 通过算数运算产生一个新的字段
FROM Product
where sale_price-cost_price>=500;
结果如下:
8、逻辑运算符(NOT、AND、OR)
上面介绍的查询条件都是单个条件,实际情况下我们可能会需要设置多个条件对数据进行筛选,这个时候就需要用到逻辑运算符NOT、AND、OR对多个条件进行逻辑运算。
NOT
NOT运算符在当前查询条件不成立时整个查询条件才成立,即取出不满足当前条件的记录,其意思相当于取反。
例如:我们想查看售价不大于5000元的销售记录。
SELECT
product_id,
product_name,
sale_price,
cost_price
FROM Product
WHERE NOT sale_price > 5000; -- (也就是sale_price<=5000)
结果如下:
AND
AND运算符在其两侧的查询条件都成立时整个查询条件才成立,其意思相当于“并且”。
例如:我们想查看“电视机”产品类别下售价在6000以上(包含)的销售数据。
SELECT
product_id,
product_name,
sale_price,
cost_price
FROM Product
WHERE product_category = '电视机'
AND sale_price >= 6000;
结果如下:
OR
OR运算符在其两侧的查询条件有一个成立时整个查询条件都成立,其意思相当于“或者”。
例如:我们想查看产品类别是“手机”或者“电脑”的销售记录。
SELECT
product_id,
product_name,
sale_price,
cost_price
FROM Product
WHERE product_category = '手机'
OR product_category = '电脑';
结果如下:
9、聚合函数(SUM/COUNT/...)
按照上面的步骤,我们已经可以按照需求设置不同的筛选条件,筛选出我们需要的数据,完成了数据提取的工作。接下来,我们就可以针对提取的数据做一些聚合操作,进行简单的分析。
常用的五个聚合函数:
COUNT:计算表中的记录数(行数)
SUM:计算表中数值列中数据的合计值
AVG:计算表中数值列中数据的平均值
MAX:求出表中任意列中数据的最大值
MIN:求出表中任意列中数据的最小值
但是需要注意的是:
COUNT(*)计算全部数据的行数(包含NULL)
COUNT(column)计算某一列的行数(不包含NULL)
COUNT(DISTINCT column)计算删除重复数据后的行数
SUM/AVG函数只能对数值类型的列使用,而MAX/MIN函数原则上可以适用于任何数据类型的列。
我们通过下面的例子展示一下各个聚合函数的使用。
SELECT
COUNT(*),-- 计算全部数据的行数(包含NULL)
COUNT(product_name), -- 计算某一列的行数(不包含NULL)
COUNT(DISTINCT product_name), -- 计算删除重复数据后的行数
SUM(sale_price),-- 计算某列所有行的总和,这里是总销售额
AVG(sale_price),-- 计算某列所有行的平均值,这里是客单价
MAX(sale_date), -- 计算某列所有行的最大值,这里是最近的销售日期
MIN(sale_date) -- 计算某列所有行的最小值,这里是最早的销售日期
FROM Product;
10、对表进行分组(GROUP BY)
GROUP BY可以像切蛋糕那样将数据进行分组,通常情况下GROUP BY和聚合函数搭配使用,用于计算在各种不同的分组下的聚合值。例如,可以按照“商品种类”product_category和“销售日期”sale_date将数据分组后再进行汇总,计算不同商品种类、每天的销售额。
使用聚合函数和GROUP BY子句时需要注意以下 4点:
1、使用GROUP BY子句时,SELECT子句中不能出现聚合键之外的列名。SELECT 子句中只能存在以下三种 元素:常数 、聚合函数和GROUP BY子句中指定的列名(也就是聚合键)。
2、在GROUP BY子句中不能使用SELECT子句中定义的字段别名。
3、GROUP BY子句结果的显示是无序的。
4、GROUP BY和WHERE并用时SELECT语句的执行顺序:
FROM → WHERE → GROUP BY → SELECT
例如:我们想查看2021-07-01以来,不同商品种类、每天的销售额。
就可以先筛选销售日期sale_date在2021-07-01以后的数据,按照“商品种类”product_category和“销售日期”sale_date将数据分组后,再对sale_price进行求和操作。
SELECT product_category,
sale_date,
SUM(sale_price)
FROM Product
WHERE sale_date > '2021-07-01'
GROUP BY product_category,
sale_date;
结果如下:
11、对聚合结果进行过滤(HAVING)
有的时候我们还需要对聚合后的数据进行过滤,什么意思呢?我们知道WHERE子句可以用来对原数据进行过滤,取出数据表中符合条件的记录,如果需要对聚合后的记录进行过滤,就需要用到HAVING子句,所以HAVING子句要写在GROUP BY子句之后。
在使用HAVING子句对聚合结果进行过滤的时候,需要注意以下几点:
1、因为HAVING子句是对聚合的结果进行过滤,所以要写在GROUP BY子句之后。
2、HAVING子句后面的聚合值不能使用别名,只能使用聚合公式,至于为什么,我们在下年SQL执行顺序部分会展开讲。
例如:上一步我们已经计算得到2021-07-01以来,不同商品种类、每天的销售额,我们现在要在这个基础上筛选出,销售额在10000以上的记录。
SELECT product_category,
sale_date,
SUM(sale_price) as GMV
FROM Product
WHERE sale_date > '2021-07-01'
GROUP BY product_category,
sale_date
HAVING SUM(sale_price)>10000 --对聚合结果进行过滤,不能使用GMV别名
结果如下:可以看到,SUM(sale_price)在10000以下的记录已经被过滤掉。
12、查询结果排序(ORDER BY)
为了符合我们查看数据的习惯,很多时候我们需要对查询的结果进行排序,就需要用到ORDER BY。
ORDER BY使用时需要注意以下几点:
1、在ORDER BY子句中列名的后面使用关键字ASC/DESC可以进行升序/降序排序,默认是升序排列。
2、ORDER BY子句通常写在SELECT语句的末尾。
3、ORDER BY 子句中同时存在多个排序列时,规则是优先使用左侧的键,以此类推。
4、在ORDER BY子句中可以使用SELECT子句中定义的字段和聚合值的别名。
为什么ORDER BY 要写在SELECT语句的末尾?为什么ORDER BY可以使用字段别名和聚合函数?这里就涉及到一个非常关键的问题:SQL语句的执行顺序,SQL语句的书写顺序如下:
SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY
但实际的执行顺序:
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
在这里可以看到,ORDER BY 是在执行的最后一步,聚合操作等已经在GROUP BY 环节完成了,且别名已经在SELECT环节生效了,所以ORDER BY这一步可以使用聚合值的别名了,当然这里也顺便解释了:为什么在使用HAVING对聚合结果过滤的时候不能使用别名,因为别名是在SELECT环节才生效的,在HAVING阶段根本就没有别名的存在。
例如:我们对上一步的结果按照日期升序、销售额降序进行展示,注意HAVING 和ORDER BY中别名的使用区别。
SELECT product_category,
sale_date,
SUM(sale_price) as GMV
FROM Product
WHERE sale_date > '2021-07-01'
GROUP BY product_category,
sale_date
HAVING SUM(sale_price)>10000 --对聚合结果进行过滤,不能使用GMV别名
ORDER BY sale_date,GMV DESC -- 按照日期升序、销售额GMV降序排列
结果如下:先按照日期升序排列,同一日期的情况下,再按照GMV降序排列
以上就是数据分析工具—SQL基础语法部分的内容,部分数据分析工具请翻看历史文章,更多数据分析工具的文章持续更新中,敬请期待,如果觉得不错,也欢迎分享、点赞和收藏哈~