背景
最近打算学习一点数据分析的内容,下图中虽然广告成分有点多,但是技多不压身个,都说人人应该有一点产品思维,并对数据保持敏感。
看了一些培训机构的介绍,涉及到的知识点还挺多的,有工具、思维、实操及最后的报告。果然一口吃不了个胖子,慢慢学吧。
数据分析框架
下面是一整套数据分析方案,分为5个步骤:明确问题、获取数据、数据清洗、分析数据最后呈现报告。
SQL语言在数据分析领域扮演着重要的角色,包括数据的存取、数据清洗、数据挖掘都会用到SQL语句。今天我分享一下我是如何学习SQL语言。学习SQL语言也分初、中、高三个阶段:
初级,大致学习一下SQL语言的基本语法,然后结合一些场景进行练习,能解决日常工作生活中的很多问题了中级篇,一些高级SQL语法,一些SQL语句常用规范,这里还不需要了解数据内核本身是如何存储数据、如何创建索引、如何解析SQL语句的,我们只需要按照前人的经验和规范来写复杂SQL就能避过不少的坑,到这个阶段就数据分析人员来说也已经很高级了,可以处理大部分场景了高级篇,对底层操作系统内核、文件系统、数据库内核有一定了解,知道如何高效的定义结构创建索引,知道执行器如何执行效率更高,能够对SQL执行的长短进行分析和调优。这个事情就交给专业的计算机或数据库系统工程师来做就行,毕竟数据分析师也有自己的专业事情要做。
总结起来就是一句话:不断的学习理论进行实践,再学习再实践。所以下面的内容也分为理论和实践两块内容来说
理论篇(SQL语言内容较多,不用全看,留着查阅就行,可以直接跳到下面的实践篇)
SQL除了查询之外还有别的功能,包括定义数据结构、修改数据库中的数据以及说明安全性约束条件
SQL查询语言概览
美国国家标准化组织(ANSI)和国际标准化组织(ISO)发布了SQL标准SQL语言有几个部分:
数据定义语言(Data-definitionlanguageDDL),定义关系模式、修改关系以及删除关系模式数据操纵语言(Data-manipulationlanguageDML),查询信息、插入元组、修改元组和删除元组完整性(integrity),SQLDDL包含了定义完整性约束的命令,保存在数据库中的数据必须满足完整性约束的定义视图定义(viewdefinition),SQLDDL包含了定义视图的命令事务控制(transactioncontrol),SQL包括了定义事务开始和结束的命令嵌入式SQL和动态SQL(embeddedSQLanddynamicSQL),定义SQL如何嵌入到通用编程语言中授权(authorization),定义对关系和视图的访问权限
本章中介绍的SQL特征基于SQL-92标准
SQL数据定义
基本类型
char(n):固定长度的字符串,用户指定长度n。也可以使用全称charactervarchar(n):可变长度的字符串,用户指定最大长度n,等价于全称charactervaryingint:整数类型(和机器相关的整数的有限自己),等价于全称integerunmeric(p,d):定点数,经度由用户指定。这个数有p位数字(加上一个符号位),其中d位数字在小数点右边。al,doublepcision:浮点数与双精度浮点数,经度与机器相关float(n):精度至少为n位的浮点数
char类型,在实际长度小于指定长度时,会在尾部填充空格,而varchar则不会
基本模式定义
下面列出完整性约束:
primarykeyfoignkey(A1,A2,An)fencenotnull
insert语句中值的顺序需要和属性在关系表中的顺序一致。droptable删除整个关系表,而deletetable只删除表中的记录
SQL查询的基本结构
SQL查询的节本结构由三个子句构成:select,from和whe。查询的输入在from子句中列出的关系
单关系查询
SQL允许在关系以及SQL表达式结果中出现重复。如果想强行删除重复,可在select后加入distinct···selectdistinct(A1,A2,An)fromtable···
select子句可以带含有+、-、*、/运算符的算术表达式,运算对象可以是常数或元组的属性。whe子句允许选出那些在from子句的结果关系中满足特定谓词的元组whe子句中允许使用逻辑连词and、or和not。逻辑连词的运算对象可以是包含比较运算符、=、、=、=和的表达式比较运算符可以用来比较字符串、算术表达式以及特殊类型,如日期类型
多关系查询
属性同时出现在多个关系表中时,关系明被用作前缀来说明我们使用的是哪个属性。这种惯例在某些情况下可能有问题,比如当需要把来自同一个关系的两个不同元组的信息进行组合时。一个SQL查询可以报货三种类型的子句:
select子句用于列出查询结果中所需要的属性from子句是一个查询求值中需要访问的关系列表whe子句是一个作用在from子句中关系的属性上的谓词
查询语句的运算顺序首先是from,然后是whe,最后是select通常说来,一个SQL查询的含义可以理解如下:
为from子句中列出的关系产生笛卡尔积(多个关系中每条元组的各种可能排列组合)在步骤1的结果上应用whe子句中指定的谓词对于步骤2结果中的每个元组,输出select子句中指定的属性
而在SQL的实际实现中,他会通过(尽可能)只产生满足whe子句谓词的笛卡尔积元素来进行优化。
自然连接
from子句中的匹配条件在最通常情况下需要在所有匹配名称的属性上相等。为了简化这种情况下SQL语句编写,SQL支持一种被称作为自然连接的运算。自然连接(naturaljoin)运算作用于两个关系,并产生一个关系作为结果。不同于两个关系上的笛卡尔积,他将第一个关系的每个元组与第二个关系的所有元组进行连接;自然连接只考虑哪些在两个关系模式中都出现的属性上取值相同的元组对。(在两个关系表中列名相同的列上取值相同的元组)为了避免不必要的同名属性列带来的危险,SQL允许用户指定需要哪些列上相等,通过joinr2using(A1,A2)
附加的基本运算
更名运算
结果中的属性名来自from子句中关系的属性名,但有的情况不能用这个方法派生名字,其原因点:
首先,from子句的两个关系中可能存在同名属性;其次select子句中使用算术表达式,那么结果属性就没有名字;再次,希望要改变结果中的属性名字。
SQL提供了一个重命名结果关系中属性的方法即使用as子句
old-nameasnew-name
as子句还可以重命名关系表,重命名关系表出于两个原因:
把长的关系名替换成短的,方便在查询的其他地方使用为了适用于需要比较同一个关系中的元组的情况,比如自己和自己进行笛卡尔积运算
被用来重命名关系的标识符在SQL标准中被称作相关名称(corlationname),也被称作表别名(tablealias)或者相关(corlationvariable)或者元组变量(tuplevariable)
字符串运算
SQL使用一对单引号来标示字符串,如果单引号是字符串的组成部分,那就用两个单引号字符来标示。SQL标准中,字符串上相等运算是大小写敏感的。部分数据库系统实现时不区分大小写,这种默认的方式是可以在数据库级或特定属性级被修改的。SQL还允许在字符串有多重函数,例如串联、提取淄川、计算字符串长度、大小写转换、去掉字符串后面的空格like操作符实现模式匹配:
百分号(%):匹配任意子串下划线(_):匹配任意一个字符
模式是大小写敏感的like比较运算中使用escape关键词来定义转义字符,举例
likeab\%descape\匹配所有以“ab%cd”开头的字符串likeab\\cd%escape\匹配所有以“ab\cd”开头的字符串
SQL允许使用notlinke比较运算符搜寻不匹配项。SQL还提供similarto操作,语法类似于UNIX中的正则表达式。
select子句中的属性说明
型号“*”表示“所有属性”,星号前面可以加上表名.代表表中所有属性,举例
selectinstructor.*frominstructor,teacheswheinstructor.ID=teaches.ID;
排列元组的显示次序
orderby子句默认使用升序,可以用desc表示降序,或者用asc表示升序
whe子句谓词
between比较运算符来说明一个值是小于或等于某个值,同时大于或等于另一个值。notbetweenwhe子句中增加一个额外条件可以用andSQL允许在元组上运用比较运算符,按字典顺序进行比较,例如:
selectname,course_idfrominstructor,teacheswhe(instructor.ID,dept_name)=(teaches.ID,Biology);
集合运算
SQL作用在关系上的union、intersect、except运算对应于数学集合论中的并、交、差
并运算
union会自动去除重复,如果想保留所有重复,需要使用unionall
(selectcourse_idfromsectionwhesemster=Fallandyear=)unionall(selectcourse_idfromsecitionwhesemestr=Springandyear=);
交运算
如果想保留重复,必须使用intersectall代替intersect
(selectcourse_idfromsectionwhesemster=Fallandyear=)intersectall(selectcourse_idfromsectionwhesemester=Springandyear=);
差运算
如果想保留重复,必须使用exceptall代替except
(selectcourse_id
fromsection
whesemster=Fallandyear=)
exceptall
(selectcourse_id
fromsection
whesemester=Springandyear=);
空值
如果算术表达式的任一输入为空,则该算术表达式结果(+、-、*、/)为空SQL将设计控制的任何比较运算的结果视为unknown,这创建了除true和false之外的第三个逻辑值布尔运算中:and:trueandunknown的结果为unknown,falseandunknown结果是false,unknownandunknown的结果是unknownor:trueorunknown的结果是true,falseorunknown的结果是unknown,unknownorunknown的结果是unknownnot:notunknown的结果是unknown如果whe子句谓词对一个元组计算出false或unknown,那么该元组不能被加入到结果集中谓词中使用特殊的关键词null测试空值如果谓词isnotnull所作用的值非空,那么它为真某些SQL实现还支持isunknown和isnotunknown来测试一个表达式结果是否为unknownselectdistinct时,如果比较的两个值非空且值相等,或者都是空,则会被认定为相同,而谓词中“null=null”会范围unknown,上述方式应用于集合的并、交和差运算
聚集函数
以值的一个集合(集或多重集)为输入,返回单个值的函数,SQL提供五个固有的聚集函数
平均值:avg最小值:min最大值:max综合:sum计数:count
基本聚集
selectcount(*)fromcourse;selectcount(distinctID)fromteacheswhesemester=Springandyear=;selectavg(aalary)frominstructorwhedept_name=Comp.Sci.;
分组聚集
在groupby子句中的所有属性上取值相同的元组将被分在一个组中任何没有出现在groupby子句中的属性如果出现在select子句中的话,它只能出现在聚集函数内部在一个特定分组(通过dept_name定义)中的每位教师都有一个不同的ID,既然每个分组只输出一个元组,那就无法确定选哪个ID值作为输出。其结果就是SQL不允许这样情况出现
having子句
having子句中的谓词在形成分组后才起作用。其限定条件是针对groupby子句构成的分组···selectdept_name,avg(salary)asorg_salaryfrominstructorgroupbydept_namehavingavg(salary);···
groupby或having子句的查询的含义可通过下述操作序列来定义:
与不带聚集的查询情况类似,最先根据from子句来计算出一个关系如果出现了whe子句,whe子句中的谓词将应用到from子句的结果关系上如果出现了groupby子句,满足whe谓词的元组通过groupby子句形成分组。如果没有groupby子句,满足whe谓词的整个元组集被当做一个分组如果出现了having子句,他将应用到每个分组上;不满足having子句谓词的分组将被抛弃select子句利用剩下的分组产生出查询结果中的元组,即在分组上应用聚集函数来得到单个结果分组
对空值和布尔值的聚集
处理空值原则:除了count(*)外的所有的聚集函数都忽略输入集合中的空值。规定空集的count运算值为0,其他所有聚集运算在输入为空集的情况下返回一个空值。SQL输入的布尔类型有三种:true,false,unknown。有两个聚集函数:some和every
嵌套子查询
子查询签到在另一个查询中的select-from-whe表达式。
集合成员资格
子查询出现在whe子句中,通过连接词in或notin测试元组是否是集合中的成员。
selectdistinctcourse_idfromsectionwhesemester=Fallandyear=andcourse_idin(selectcourse_idfromsectionwhesemester=Springandyear=);
集合的比较
SQL允许some,=some,=some。=some和some。(至少比其中的某一个大、小、等于)=some等价于in,然后some不等价于notin举例:找出工资至少比Biology系某一个教师工资高的所有教师的姓名
selectnamefrominstructorwhesalarysome(selectsalayfrominstructorwhedept_name=Biology);
SQL允许all,=all,=all,=all和all,all等价于notin,但=all并不等价于in举例:找出工资比Biology系每个教师工资高的所有教师的姓名
selectnamefrominstructorwhesalaryall(selectsalayfrominstructorwhedept_name=Biology);
空关系测试
exists结构在作为参数的子查询非空时返回true值举例:找出在年秋季学期和年春季学期同时开课的所有课程
selectcorse_idfromsectionasSwhesemester=Fallandyear=andexists(select*fromsectionasTwhesemester=Springandyear=andS.course_id=T.course_id);
来自外层查询的一个相关名称(上述查询中的S)可以用在whe子句的子查询中。使用了来自外层查询相关名称的子查询被称作相关子查询(corlatedsubquery)在一个子查询中只能使用此子查询本身定义的,或者在包含此子查询的任何查询中定义的相关名称。如果一个相关名称既在子查询中定义,又在包含该子查询的查询中定义,则子查询中的定义有效。这条规则类似于编程语言中通用的变量作用于规则。notexists结构测试子查询结果集中是否不存在元组。可以使用notexists结构模拟集合包含(即超集)操作。可以将“关系A包含关B”写成“notexists(BexceptA)”举例:找出选修了Biology系开设的所有课程的学生
selectS.ID,S.namefromstudentasSwhenotexists((selectcourse_idfromcoursewhedept_name=Biology)except(selectT.course_idfromtakesasTwheS.ID=T.ID));
这里,子查询
(selectcourse_idfromcoursewhedept_name=Biology)找出Biology系开始的所有课程集合,子查询(selectT.course_idfromtakesasTwheS.ID=T.ID)
找出S.ID选修的所有课程。这样,外层select对每个学生测试其选修的所有课程集合是否包含Biology系开始的所有课程集合
这个逻辑感觉理解不了!!!
重复元组存在性测试
unique结构用来判断作为参数的子查询结果中有没有重复元组,如果没有返回true否则返回flase举例:找出所有在年最多开始一次的课程
selectT.course_idfromcourseasTwheunique(selectR.course_idfromsectionasRwheT.course_id=R.course_idandR.year=);
unique谓词在空集合上计算出真值notunique结构测试在一个子查询结果中是否存在重复元组注意如果关系中存在两个元组t1和t2,如果t1或t2的某个域为空时,判断t1=t2为假尽管一个元组有多个副本,只要该元组一个属性为空,unique测试就有可能为真
from子句中的子查询
任何select-from-whe表达式返回的结果都是关系,因此可以被插入到另一个select-from-whe中任何关系可以出现的位置举例:找出哪些系中教师的平均工资超过
selectdept_name,avg_salaryfrom(selectdept_name,avg(salary)asavg_salaryfrominstructorgroupbydept_name)wheavg_salary;
举例:找出所有系中工资总额最大的系,此时having子句是无法解决的智能用from子句中的子查询
selectmax(tot_salary)from(selectdept_name,sum(salary)frominstructorgroupbydept_name)asdept_total(dept_name,totl_salary);
SQL允许from子句中的子查询用关键词lateral作为前缀,以便访问from子句中再它前面的表或子查询中的属性。举例:打印每位老师的姓名,以及他们的工资和所在系的平均工作
selectname,salary,avg_salaryfrominstructorI1,lateral(selectavg(salary)asavg_salaryfrominstructorI2wheI2.dept_name=I1.dept_name);
with子句
with子句提供定义临时关系的方法,这个定义只对包含with子句的查询于晓举例:找出具有最大预算值的系
withmax_budget(value)as(selectmax(budget)fromdepartment)selectbudgetfromdepartment,max_budgetwhedepartment,budget=max_budget.value;
举例:查出所有工资总额大于所有系平均工资总额的系
withdept_total(dept_name,value)as(selectdept_name,sum(salary)frominstructorgroupbydept_name),dept_total_avg(value)as(selectavg(value)fromdept_total)selectdept_namefromdept_total,dept_total_avgwhedept_total.value=dept_total_avg.value;
标量子查询
SQL允许子查询出现在返回单个值的表达式能够出现的任何地方,只要该子查询只返回包含单个属性的单个元组;这样的子查询称为标量子查询(scalarsubquery)举例:列出所有的系以及他们拥有的教师数···selectdept_name,(selectcount()frominstructorwhedepartment.dept_name=instructor.dept_name)asnum_instructorsfromdepartment;···该例子中子查询保证只返回单个值,因为他使用了不带groupby的count()聚集函数标量子查询可以出现在select、whe、having子句中,在编译时并非总能判断一个子查询返回的结果中是否有多个元组,如果子查询被执行后其结果不止一个元组,则产生一个运行时错误。
注意从技术上来讲标量子查询的结果类型仍然是关系,尽管其中只包含单个元组。然后当在表达式中使用标量子查询时,他出现的位置是单个值出现的地方,SQL就从该关系中包含单属性的单元组中取出相应的值,并返回该值。
数据库的修改
删除
只能删除单个元组,不能只删除某些属性上的值
deletefromrwheP;
delete命令只能作用于一个关系,如果想从多个关系中删除元组,必须在每个关系上使用一个delete命令。delete的whe子句中可以嵌套select-from-whe
举例:删除工资低于大学平均工资的教师记录
deletefrominstructorwhesalary(selectavg(salary)frominstructor);
在删除之前先进行所有元组的测试很重要,在上面的例子中若有些元组在其余元组未被测试前被删除,则平均工资将会改变,这样delte的最后结果将依赖于玉足被处理的顺序。
插入
可以指定待插入的元组,或者写一条查询语句来生成待插入的元组集合。考虑到用户可能不记得关系属性的排列顺序,SQL允许在insert语句中指定属性···insertintocourse(course_id,title,dept_name,cdis)values(CS-,DatabaseSystem,