数据与智能本微信官方账号专注于大数据和人工智能技术。一批有多年实践经验的技术极客参与运营管理,在大数据、数据分析、推荐系统、机器学习、人工智能等方向持续输出原创文章。每周输出至少10篇优秀原创文章。同时,我们将关注和分享大数据和人工智能的行业趋势。欢迎关注。
作者:凯连凯
导语
本文是《大数据分析师入门课程》系列的第三篇,主要讲解大数据分析师必须了解的SQL基础知识。如果你不了解这门课程,建议你先看《大数据分析工程师入门--0.开篇词》这篇文章,了解课程的设计思路和目标。如果你再读一遍这篇文章,就会更容易理解这篇文章的内容和目标。
先看三个问题。
1.为什么要谈SQL?
2.这篇论文的主要目标是什么?
3.这篇文章的主旨是什么?
首先,SQL是一种表现力非常强的数据分析语言。大多数常用的数据分析方法都可以用SQL来表达。
其次,SQL的语法有国际标准,非常简洁统一,易写,易学易用。所以更容易理解。HiveQL和SparkSQL的语法大多遵循SQL的国际标准,所以学习SQL然后学习HiveQL和SparkSQL很容易。
还有,SQL运行起来非常方便,有各种成熟的工具。写好之后就可以轻松运行调试了。
因此,SQL在大数据分析师的日常工作中被广泛使用和频繁使用。
总之,
1.数据库系统的一些基本概念。
2.2的基本语法。SQL查询
3.数据库函数、谓词和CASE表达式
4.关联查询和子查询
第1部分,数据库体系的一些基本概念。:我们将提到一些与数据库相关的重要概念,这样我们就能理解如何编写SQL。更重要的是,理解这些概念是和别人交流SQL的前提。
第2部分,SQL查询的基本语法。:我们将解释如何使用SELECT子句,以及如何在单个表上完成一些简单的统计分析。
第3部分,数据库函数、谓词和CASE表达式。:我们来介绍一些常用的函数,判断真假的谓词,多条件判断的CASE表达式。
第4部分,关联查询和子查询。:我们将带您学习如何连接多个表,通过表交叉获得更多信息,并继续使用子查询分析查询结果。
由于不同的数据库厂商有不同的引擎实现,SQL的语法、关键字、函数也略有不同,本文仅以互联网公司中应用最广泛的MySQL为例进行说明。文中涉及的SQL和例子都是在MySQL中运行的。下面进入正式的知识讲解。
正式开讲啦
关系型数据库中的表,通常是指由行和列组成的用于存储数据的二维表。表是数据存储的直接载体,我们的数据通常都需要存储在表中。数据库基本上都是通过表来组织数据的。所以,表也是我们查询并获取数据最直接的对象。
对于表而言,有以下几个特性:
a.表是由存在关联性的多列组成的,可以存储N多行数据,每行数据称为一条记录,行和列的交叉点唯一确定一个单元格
b.表中的列名不重复,即列名需唯一
c.表中的任意一列都只能存储一种数据类型的数据
在不同的数据库管理系统中,支持的数据类型会略有差异,本文就以MySQL为例,介绍几种最常用的数据类型,分别如下所示:
上面只是罗列出了几种最常用的数据类型,如果大家接触到了其他不常用的类型,可以自己在网上搜索一下相关的信息。
主键是一列或多列的组合,用于标识表中唯一的一条记录。所以,它天然的一个属性就是不重复性,也不允许为NULL值。通常我们会使用自增的整型值来作为主键,由数据库管理系统来维护,既能保证唯一性,又使用起来很方便。一个表的主键,通常也会作为其他表引用的对象,即后面要讲到的外键。
外键通常用来建立两张表之间的关联关系,一个表的外键通常是与之关联的另一个表的主键。这样在进行关联查询时,就可以通过两个表外键和主键之间的关系,将两张表连接起来,形成一张中间表,将两张表的信息融合,产生更大的价值。
如果你想快速找到一本书中,你感兴趣的部分,你就会去查找目录,目录可以帮你快速定位到你想看的内容在哪一页。对于数据库中的表来说,索引就相当于是表的目录。其存在的主要目的就是为了加快查询速度。当然,索引也还有一些其他用途,其设计原理也是非常巧妙,我们会在下一篇SQL进阶文章中,详细讲解这块内容。
在关系型数据库中,表和表之间的关系通常有三种,1对1、1对多、多对多。为方便描述,我们假定有两张表,分别为表A和表B。
1对1,是指表A和表B通过某字段关联后,表A中的一条记录最多对应表B中的一条记录,表B中的一条记录也最多对应表A中的一条记录。
1对多,是指表A和表B通过某字段关联后,表A中的一条记录可能对应表B中的多条记录,而表B中的一条记录最多对应表A中的一条记录。
多对多,是指表A和表B通过某字段关联后,表A中的一条记录可能对应表B中的多条记录,而表B中的一条记录可能对应表A中的多条记录。
1对1和1对多关系,通常使用外键引用对应表的主键就可以表达。而多对多关系,通常需要使用中间表来表达,中间表中记录了两张表的主键的对应关系。
如果一条SQL的结果在日常查询中经常被用到,我们通常就会考虑使用视图将其存储起来,下次再使用时直接读取视图,就会执行视图对应的SQL语句,非常地简洁方便。所以,视图就是一张虚拟的表。不过,值得注意的是,视图存储的是SQL语句,而不是SQL执行后的结果,其结果是每次执行时动态生成的,可能每次读取都会有变化。
集合在数学领域表示“(各种各样的)事物的总和”,在数据库领域表示记录的集合。具体来说,表、视图和查询的执行结果都是记录的集合。是集合,就可以进行集合运算,如求并集、交集、差集等。另外,查询的执行结果也是集合,那么就可以把查询的结果再当做一个表,继续基于这个表做分析。这个便是子查询的理论基础。在第4部分,我们会详细讲到子查询。
SQL是结构化查询语言(StructuralQueryLanguage)的简称,是开发者与数据库管理系统对话的语言。SQL用关键字、表名、列名、操作符等组合而成的一条语句,用来描述操作的内容。SQL是有国际标准的,因此其通用性不言而喻。
SQL有很多关键字,每个关键字的含义和用法都不相同。本文只罗列出在数据分析工作中与查询分析相关的常用的基础关键字及其含义,其中有一些会在下面的段落中详细讲解,如下表格所示:
最简单的查询语句莫过于"SELECT*FROMA",其中A表示数据表名A,这条
SQL的含义是从表A中查询出所有列的所有数据。"*"代表表A中的所有列,是一种简写形式。我们就从这条最简单的SQL开始,逐渐添加关键字,最后变成一条复杂的SQL。
接下来我们要讲解的简单查询,都是针对单个表的查询。针对单表的查询虽然比较简单,但是却是复杂查询的基础。为了方便演示,我们先定义一个数据表student,用于存储学生的信息,表里的数据如下所示:
从左到右列的含义依次为学号、姓名、年龄、英语成绩、数学成绩、总成绩。
如果要对表中的数据进行过滤,只保留满足我们需求的数据,那就要用到WHERE关键字了。WHERE关键字后跟的是由逻辑运算符连接的一个或多个表达式,每个表达式的最终结果为TRUE或FALSE,只保留表达式结果为TRUE的行。
例如,我们要获取英文成绩不合格的学生姓名和学号,则对应的SQL为
SELECTsno,nameFROMstudentWHEREeng_score<60运行结果如下:
运算符,顾名思义就是用于做运算的符号。常见的运算符有三种,比较运算符、算术运算符和逻辑运算符。
不等于的判断,目前绝大部分的数据库管理系统厂商也都支持了"!="运算符,与"<>"表达的含义相同。
算术运算符在书写时可以紧挨着字段名写,如eng_score-math_score,所以字段名和表名的命名中不能使用中划线("-"),否则它会被误判为是在做减法运算的。
当存在多种逻辑运算符时,为了避免歧义,需要使用括号来界定执行的先后顺序,使用括号组织的表达式,可读性也会更强。建议大家不要去记忆逻辑运算符的优先级,容易记混,而且写出的SQL可读性比较差,最好是使用括号,来厘清多个逻辑条件的关系,清晰易懂,可读性强,不容易出错。
了解了上面这些运算符,我们便可以通过组合各种运算符,书写出WHERE后面复杂的表达式,来满足我们的过滤需求了。
分组聚合是指,我们可以将表中的数据,根据某一列或多列进行分组,然后将其他列的值进行聚合计算,如计数、求和和求平均值等。用到的关键字是GROUPBY,对于分组后的计算结果,我们还可以使用HAVING进行过滤。
例如,从student表中,求出不同年龄的人数、英语总成绩和数学成绩的平均值,且过滤掉。对应的SQL为
SELECTage,COUNT(sno)ASstudent_num,SUM(eng_score)ASsum_eng_score,AVG(math_score)ASavg_math_scoreFROMstudentGROUPBYageHAVINGavg_math_score>=60运行后结果如下所示:
这里需要注意的是,出现在groupby后面的字段或计算公式,必须出现在对应的select的后面,并且除了这些字段或计算公式外,select后面不能有其他字段,只能使用聚合函数。
DISTINCT关键字用于对一列或多列去重,返回剔除了重复行的结果。DISTINCT对多列去重时,必须满足每一列都相同时,才认为是重复的行进行剔除。DISTINCT不会过滤掉NULL值,但去重后的结果只会保留一个NULL值。
例如,从student表中,找出有几种年龄的学生,即求出去重后的年龄。对应的SQL为
SELECTDISTINCTageFROMstudent运行后的结果如下所示:
日常生活场景里,我们经常对各种各样的排名比较感兴趣,比较关注排在前面的内容。在数据库中,求出排名,就需要用到ORDERBY子句。ORDERBY通常配合ASC和DESC使用,可以根据一列或多列,进行升序或降序排列,之后使用LIMIT取出满足条件的前N行。
例如,从student表中,求出数学成绩最好的前3名学生的姓名、年龄和其数学成绩。对应的SQL如下:
SELECTname,age,math_scoreFROMstudentORDERBYmath_scoreDESCLIMIT3运行后的结果为:
增加常量列,即把某一固定的常量值做为一列添加到我们的结果数据中。这种做法的应用场景,通常是结果集中所有的行在某个属性上值是相同的,这时便可以通过增加常量列的方式,来增加这一列。我们通过下面的例子来演示其语法形式。
例如,从student表中,查询英语成绩大于80分的学生的姓名和学号,并把他们都分入A班。对应的SQL如下:
SELECTsno,name,'A'ASclassFROMstudentWHEREeng_score>80运行后的结果为:
从示例中可以看出,直接通过"常量AS新列名"的方式就可以增加常量列,非常地方便。
SQL之所以具有强大的分析表达能力,其中一个重要原因,就是它具备丰富的函数,通过这些函数的组合可以实现对数据的复杂处理,最终得到我们想要的数据。另外一方面,SQL也有丰富的谓词来对数据进行判断,匹配出符合我们需求的数据。CASE表达式是一种多条件判断表达式,可以根据不同条件返回不同的值,类似于编程语言中的IFELSE。
聚合函数,又称分析函数,是将一组值通过聚合分析后得到一个值,因此得名聚合函数。使用频率最高的聚合函数有5个,如下表所示
聚合函数有一个共同的特点,即在计算过程中都会忽略掉NULL值,因为对NULL的聚合是没有任何意义的。COUNT、SUM和AVG三个函数还可以和DISTINCT配合使用,其含义为先对目标列进行去重,之后再对去重后的结果聚合。SUM和AVG只能应用于一列,且列的数据类型为数值型。MIN和MAX也是只能应用于一列,不过除了支持数值型外,还支持字符串类型和日期类型。COUNT可以应用于一列或多列,而且不限制列的类型。
算术函数,主要用于对数值类型进行各种数学运算。SQL中除了加减乘除(+-*/)四个运算符外,还提供了一系列的算术函数,如下表所示:
这里只罗列了常用的一些函数,通过他们之间的组合,可以实现复杂的运算,如果上述表格不满足你的分析需求,可以自行Google或查看官方文档,寻找匹配的算术函数。
日常分析工作中,经常需要对日期进行加减、格式化等处理,这就离不开强大的日期处理函数,常用的日期函数如下:
日期函数用于获取当前日期时间的函数多数是空参数函数,虽然参数为空,但是函数名后的括号不能省略不写。数据库厂商虽然也提供了部分与函数名相同的属性值,不带括号也能调用,不过笔者建议最好还是使用函数带上空括号,这样识别度更高,可读性更好。
字符串是信息的一个重要载体,其中包含着大量的重要信息,因此对字符串的处理非常重要,相应地字符串处理函数也是非常丰富,以下我们罗列出最常用的一些函数:
3.5转换函数
当某些数据的类型与我们需要的类型不符时,可以使用类型转换函数,将其类型转换为我们需要的类型。常用的类型转换函数有两个,分别为CAST和CONVERT,两个函数的作用是相同的,只是语法略有不同。CAST函数的用法为CAST(字段AS数据类型),而CONVERT的用法为CONVERT(字段,数据类型)。
不过,并不是所有的类型都是可以互相转换的,而且有些转换会导致精度的损失,因此请谨慎使用。
还有一些函数是使用在特定用途上的,本文也罗列出几个数据分析工作中常用的。
MD5函数,其作用是生成等长的信息摘要。在数据分析工作中,经常用于对敏感信息的脱敏,因为很难通过md5值反向推断加密前的内容,因此是非常安全的。其使用方法为,MD5(str),返回对str进行md5算法计算得到的校验和字符串。
IFNULL(expr1,expr2):如果expr1不为NULL,则返回expr1,否则返回expr2。通常用于对某个字段的NULL值填补,也叫缺失值填补。
IF(expr1,expr2,expr3):如果expr1不等于0或者不为NULL,则返回expr2的值,否则返回expr3的值。相当于编程语言中的IFELSE条件判断语句
简单来说,谓词就是用于真假判断的关键字,用来判定两个对象间关系论断的真假,返回值只有真或假。这么说可能还是有点抽象。我们来举一些谓词的例子大家就明白了。
例如,我们前面讲到的比较运算符,就都属于谓词的范畴。还有一些其他谓词如下表所示:
SQL语句中的CASE表达式,对应着编程语言中的条件分支,起到多条件判断返回多种值的作用。其语法形式为:
CASE
WHEN<求值表达式1>THEN<表达式1>
WHEN<求值表达式2>THEN<表达式2>
WHEN<求值表达式3>THEN<表达式3>
......
ELSE<表达式>END
其执行过程为,按照书写顺序,依次判断WHEN后面求值表达式返回的值为真或假,如果返回值为假,则继续向下搜索;如果返回值为真时,执行THEN后面对应的表达式,将执行后的值返回,CASE表达式退出;如果所有WHEN子句都不满足时,则执行ELSE后面的表达式,返回执行后得到的值,CASE表达式退出。
了解了执行过程,那么在书写CASE表达式时,就一定要注意顺序问题。这里需要注意一点的是,如果执行到第二个THEN的时候,实际生效的条件为<求值表达式1>的值为假,与此同时<求值表达式2>的值为真;如果执行到第三个THEN的时候,实际生效的条件为<求值表达式1>和<求值表达式2>的值都为假,与此同时<求值表达式3>的值为真,往后以此类推。
为了方便理解,这里我们举个例子。例如,从student表中,对学生进行成绩的等级划分,取出学生姓名及成绩等级。划分规则为两门成绩都大于等于80分为优,两门成绩都大于等于60分但至少一门未达到80分的为良,一门成绩不及格为中,两门都不及格为差。对应的SQL如下:
SELECTname,CASEWHENmath_score>=80ANDeng_score>=80THEN'优'WHENmath_score>=60ANDeng_score>=60THEN'良'WHENmath_score>=60OReng_score>=60THEN'中'WHENmath_score<=60ANDeng_score<60THEN'差'ELSENULLENDASscore_gradeFROMstudent运行后的结果为:
这里当判定Alice的成绩等级为“良”时,已经判断过她不会为优了,即不可能两门都超过80分,也就是至少有一门低于80分了,所以就不需要再限定有一门成绩要低于80分了。
NULL值的判断必须使用谓词IS,因为NULL和其他任何值(包括NULL值)比较结果都为NULL,也就对应着假。这一点很好理解,你可以把NULL值理解为未知。未知和任何值比较结果还是未知,未知和未知比较,结果也只能是未知。
拥有了前面3部分的知识基础,那么我们就可以开始学习SQL的复杂查询。本文要讲的复杂查询有两个,一个是关联查询,一个是子查询。首先,我们先来看下他们的理论基础,集合运算。
在第1部分,我们提到过,在数据库领域,集合是指一组记录的总和,它可以指代表,也可以指代视图、查询执行的结果。所以,表和查询执行的结果都是集合,那么就都可以参与集合运算。也就是说,可以把查询执行的结果看做是一张中间表或临时表,继续参与运算,这就是子查询的理论基础。
集合运算主要包含四种,并集、交集、差集和笛卡尔积。
并集,是求两个集合合并后的集合。在MySQL中使用关键字UNION或UNIONALL实现,两者的区别是,UNION会剔除掉合并后集合中的多余重复值,只保留一份;而UNIONALL,不会剔除重复值。因此,UNION操作,运行结束后,可能会导致记录数的减少。
交集,是求两个集合都共同拥有的元素的集合。在MySQL中没有提供专门的关键字,而是通过内关联实现的,下一小结会详细介绍。
差集,是求在一个集合中存在而在另一个集合中不存在的元素的集合。差集计算具有方向性,同样的,MySQL也没有提供差集计算的关键字,而是需要通过左/右关联然后再过滤出未关联成功的记录而得到。
笛卡尔积,是将两个集合中记录两两组合,相当于集合的乘法。它是关联查询的数学理论基础。你可以简单理解为,关联查询的过程就是,先做笛卡尔积,然后再通过on条件过滤出符合条件的记录。当然,实际的执行过程,不会这么简单,但是是在这个流程基础上去做优化,减少计算量的。
在进行集合的并集、交集和差集运算时,需要注意的是:
常见的表关联类型有四种,内连接(INNERJOIN)、左外连接(LEFTOUTERJOIN)、右外连接(RIGHTOUTERJOIN)、全外连接(FULLOUTERJOIN)。
关联的语法比较简单,拿内连接举例,书写为,AINNERJOINBONexpr。其中,A和B表示两个表的名称,也可以是子查询。ON后面跟的expr表示关联条件,通常是由表A和表B关联字段组成的表达式。
内连接(INNERJOIN),通常可以省略掉INNER不写,它的含义是左右两个集合相乘后,只保留满足ON后面关联条件的记录。所以,可以利用内连接计算两个集合的交集,只需要把集合元素的字段都写在ON后面的关联条件里即可。
左外连接(LEFTOUTERJOIN),OUTER通常可以省略不写,它的含义是,左右两个集合相乘后,保留满足ON后面关联条件的记录加上左表中原有的但未关联成功的记录。因此,左外连接,可以用来计算集合的差集,只需要过滤掉关联成功的记录,留下左表中原有的但未关联成功的记录,就是我们要的差集。
右外连接(RIGHTOUTERJOIN),与左外连接含义相同,只是方向不同而已,通常也是省略OUTER不写。
全外连接(FULLOUTERJOIN),含义是,左右两个集合相乘后,保留满足ON后面关联条件的记录加上左表和右表中原有的但未关联成功的记录。
4种JOIN方式的示意图
还有一种交叉连接,不过不常用。它的含义是产生笛卡尔积,由于其结果量级可能非常大,存在一定的计算风险,因此非常少用,如果要使用,一定要事先评估好,参与运算的集合的大小。
从另一个角度来划分,连接还分为等值连接和不等值连接。这是由ON后面的子句决定的,如果关联字段使用等号判断是否相等就是等值连接,如果使用其他判断符号(如大于、小于等),则为不等值连接。
多表关联的本质,还是两两关联。例如,表A内关联表B再内关联表C,实际上就可以等价于表A内关联表B,运行后的结果作为一张中间表,然后再与表C内关联。所以,执行过程仍然是两两关联。
表关联是比较复杂的查询方式,在书写时,大家要在脑海中构建关联后的集合的样子,对应去选择需要使用的连接方法。下面是根据实际工作经验总结的容易出错的点,希望大家注意。
a.使用UNION可能会导致记录数的减少,在使用聚合函数时,可能会导致计算出现偏差
b.在使用1对多或多对多关系的表进行关联时,记录数可能会增多,也可能会导致计算出现偏差
c.左外连接和右外连接都有连接方向的问题,表放的位置对结果是有影响的,尤其是多表关联时,一定要关注书写的顺序,尽可能先做内连接再做左/右外连接。
d.尽量避免使用交叉连接
至于上述a和b问题怎么解决,就需要具体问题具体分析了,这里就不展开讲解了。
子查询,就是指被括号嵌套起来的查询SQL语句,通常是一条完整的SELECT语句。
子查询放在不同的位置,起到的作用也是不同的。它经常出现在3个位置上,分别是SELECT后面、FROM/JOIN后面,还有WHERE/HAVING后面。
当子查询出现在SELECT后面时,其作用通常是要为结果添加一列。不过,这里要注意的是,在SELECT后使用的子查询语句只能返回单个列,且要保证满足条件时子查询语句只会返回单行结果。企图检索多个列或返回多行结果将引发错误。
子查询出现在FROM/JOIN后面,是我们最常用的方式,就是将子查询的结果作为中间表,继续基于这个表做分析。
当子查询出现在WHERE/HAVING后面时,则表示要使用子查询返回的结果做过滤。这里根据子查询返回的结果数量,分三种情况,即1行1列、N行1列、N行N列。
当返回结果为1行1列时,实际上就是返回了一个具体值,这种子查询又叫标量子查询。标量子查询的结果,可以直接用比较运算符来进行计算。
当返回结果是N行1列时,实际上就是返回了一个相同类型数值的集合。因此可以使用IN谓词判断,同时也可以配合ANY、SOME、ALL等关键字使用。
当返回结果是N行N列时,实际上就是返回一个临时表,这时就不能进行值的比较了,而是使用EXISTS谓词判断返回的集合是否为空。
总结
本文介绍了SQL的基础知识,带领大家一起了解一些重要的数据库概念,理解这些概念是与他人在工作中协作和顺畅沟通的前提;我们讲解了SQL的基本语法,将SQL的各个部分拆解开来,详细介绍了各种常用关键字的含义,并举了一些事例,方便大家理解,建立基本概念;我们一起学习了数据库函数、谓词和CASE表达式,了解了SQL的强大分析表达能力,为数据分析工作奠定了基础;最后,我们讲解了关联查询与子查询,为大家的装备库又增加了一把利刃,实现更加复杂的分析。
参考文献:
[1]《数据库原理及应用》主编:何玉洁、刘福刚
[2]《SQL必知必会》作者:福塔
[3]《SQL基础知识》作者:MICK
上一篇:中俄cr929客机