SQL基础教程(第2版)
0-2 通过PostgreSQL执行SQL语句
- 数据库的名称只能使用小写字母
1-1 数据库是什么
- 像这样将大量数据保存起来,通过计算机加工而成的可以进行高效访问的数据集合称为数据库(Database, DB)
- 实现读写自动化需要编程能力
- 层次数据库(Hierarchical Database, HDB)
- ● OracIe Database:甲骨文公司的RDBMS● SQL Server:微软公司的RDBMS● DB2:IBM公司的RDBMS● PostgreSQL:开源的RDBMS● MySQL:开源的RDBMS
- 面向对象数据库(Object Oriented Database, OODB)
- XML数据库(XML Database, XMLDB)
- 键值存储系统(Key-Value Store, KVS)
1-2 数据库的结构
- 使用RDBMS时,最常见的系统结构就是客户端/服务器类型(C/S类型)这种结构
- 服务器指的是用来接收其他程序发出的请求,并对该请求进行相应处理的程序(软件),或者是安装了此类程序的设备(计算机)
- 向服务器发出请求的程序(软件),或者是安装了该程序的设备(计算机)称为客户端。
- 用来管理数据的二维表在关系数据库中简称为表。
- 根据SQL语句的内容返回的数据同样必须是二维表的形式
- 表的列(垂直方向)称为字段
- 关系数据库必须以行为单位进行数据读写
- 关系数据库以行为单位读写数据。
- 行和列交汇的方格称为单元格。一个单元格中只能输入一个数据
- 一个单元格中只能输入一个数据。
1-3 SQL概要
- 原则上SQL语句都会使用分号结尾。
- 学会标准SQL就可以在各种RDBMS中书写SQL语句了。
- SQL用关键字、表名、列名等组合而成的一条语句(SQL语句)来描述操作的内容。
- DDL(Data Definition Language,数据定义语言)用来创建或者删除存储数据用的数据库以及数据库中的表等对象。
- SQL根据功能不同可以分为三类,其中使用最多的是DML。
- SQL语句要以分号(;)结尾
- SQL语句以分号(;)结尾。
- SQL语句不区分大小写
- SQL 不区分关键字的大小写。
- ● 关键字大写● 表名的首字母大写● 其余(列名等)小写
- 常数的书写方式是固定的
- SQL语句中含有字符串的时候,需要像’abc’这样,使用单引号(')将字符串括起来,用来标识这是一个字符串。
- 字符串和日期常数需要使用单引号(')括起来。数字常数无需加注单引号(直接书写数字即可)。
- 单词需要用半角空格或者换行来分隔
- SQL语句的单词之间需使用半角空格或换行符来进行分隔
- 单词之间需要使用半角空格或者换行符进行分隔。
1-4 表的创建
- 我们只能使用半角英文字母、数字、下划线(_)作为数据库、表和列的名称。
- 数据库名称、表名和列名等可以使用以下三种字符。● 半角英文字母 ● 半角数字 ● 下划线(_)
- 名称必须以半角英文字母作为开头。
- 在同一个数据库中不能创建两个相同名称的表,在同一个表中也不能创建两个名称相同的列
- 数据类型表示数据的种类,包括数字型、字符型和日期型等
- INTEGER型用来指定存储整数的列的数据类型(数字型),不能存储小数。
- 约束是除了数据类型之外,对列中存储的数据进行限制或者追加条件的功能。
1-5 表的删除和更新
- DROP在英语中是“丢掉”“舍弃”的意思。需要特别注意的是,删除的表是无法恢复的[插图]。即使是被误删的表,也无法恢复,只能重新创建,然后重新插入数据。
- 删除了的表是无法恢复的。在执行DROP TABLE语句之前请务必仔细确认。
- ALTER TABLE语句和DROP TABLE语句一样,执行之后无法恢复
- 表定义变更之后无法恢复。在执行ALTER TABLE语句之前请务必仔细确认。
- 开头的BEGIN TRANSACTION语句是开始插入行的指令语句,结尾的COMMIT语句是确定插入行的指令语句。
2-1 SELECT语句基础
- 从表中选取数据时需要使用SELECT语句,也就是只从表中选出(SELECT)必要数据的意思。
- 查询结果中列的顺序和SELECT子句中的顺序相同[插图]。
- 想要查询出全部列时,可以使用代表所有列的星号(*)。
- 如果使用星号的话,就无法设定列的显示顺序了。这时就会按照CREATE TABLE语句的定义对列进行排序。
- 别名可以使用中文,使用中文时需要用双引号(")括起来[插图]。请注意不是单引号(')。
- 设定汉语别名时需要使用双引号(")括起来。
- SELECT子句中不仅可以书写列名,还可以书写常数
- 在SELECT语句中使用DISTINCT可以删除重复行。
- 在使用DISTINCT时,NULL也被视为一类数据。
- DISTINCT关键字只能用在第一个列名之前。
- SELECT语句通过WHERE子句来指定查询数据的条件。
- 首先通过WHERE子句查询出符合指定条件的记录,然后再选取出SELECT语句指定的列
- WHERE子句要紧跟在FROM子句之后。
2-2 算术运算符和比较运算符
- SELECT子句中可以使用常数或者表达式。
- 所有包含NULL的计算,结果肯定是NULL。
- 我们使用“不等于”这样代表否定含义的比较运算符<>
- 一定要让不等号在左,等号在右。
- 使用比较运算符时一定要注意不等号和等号的位置。
- 字符串类型的数据原则上按照字典顺序进行排序,不能与数字的大小顺序混淆。
- 不能对NULL使用比较运算符
- 即使使用<>运算符也还是无法选取出NULL的记录[插图]。因此,SQL提供了专门用来判断是否为NULL的IS NULL运算符。
- 希望选取NULL记录时,需要在条件表达式中使用IS NULL运算符。希望选取不是NULL的记录时,需要在条件表达式中使用IS NOT NULL运算符。
2-3 逻辑运算符
- NOT不能单独使用,必须和其他查询条件组合起来使用。
- NOT运算符用来否定某一条件,但是不能滥用。
- AND运算符在其两侧的查询条件都成立时整个查询条件才成立,其意思相当于“并且”。OR运算符在其两侧的查询条件有一个成立时整个查询条件都成立,其意思相当于“或者”
- 多个查询条件进行组合时,需要使用AND运算符或者OR运算符。
- AND运算符优先于OR运算符
- AND运算符的优先级高于OR运算符。想要优先执行OR运算符时可以使用括号。
- 使用AND运算符进行的逻辑运算称为逻辑积,使用OR运算符进行的逻辑运算称为逻辑和。
- 与通常的逻辑运算被称为二值逻辑相对,只有SQL中的逻辑运算被称为三值逻辑。
- 数据库领域的有识之士们达成了“尽量不使用NULL”的共识。
3-1 对表进行聚合查询
- 通常,聚合函数会对NULL以外的对象进行汇总。但是只有COUNT函数例外,使用COUNT(*)可以查出包含NULL在内的全部数据的行数。
- COUNT:计算表中的记录数(行数) SUM:计算表中数值列中数据的合计值 AVG:计算表中数值列中数据的平均值 MAX:求出表中任意列中数据的最大值 MIN:求出表中任意列中数据的最小值
- 对于COUNT函数来说,参数列不同计算的结果也会发生变化
- COUNT函数的结果根据参数的不同而不同。COUNT(*)会得到包含NULL的数据行数,而COUNT(<列名>)会得到NULL之外的数据行数。
- 聚合函数会将NULL排除在外。但COUNT(*)例外,并不会排除NULL。
- 计算进货单价平均值的情况与SUM函数相同,会事先删除NULL再进行计算
- MAX/MIN函数和SUM/AVG函数有一点不同,那就是SUM/AVG函数只能对数值类型的列使用,而MAX/MIN函数原则上可以适用于任何数据类型的列。
- MAX/MIN函数几乎适用于所有数据类型的列。SUM/AVG函数只适用于数值类型的列。
- 想要计算值的种类时,可以在COUNT函数的参数中使用DISTINCT。
- 在聚合函数的参数中使用DISTINCT,可以删除重复数据。
3-2 对表进行分组
- 使用聚合函数和GROUP BY子句时需要注意以下4点。① 只能写在SELECT子句之中② GROUP BY子句中不能使用SELECT子句中列的别名③ GROUP BY子句的聚合结果是无序的④ WHERE子句中不能使用聚合函数
- 在GROUP BY子句中指定的列称为聚合键或者分组列
- GROUP BY就像是切分表的一把刀。
- GROUP BY子句的书写位置也有严格要求,一定要写在FROM语句之后(如果有WHERE子句的话需要写在WHERE子句之后)
- SELECT→2. FROM→3. WHERE→4. GROUP B
- 聚合键中包含NULL时,在结果中会以“不确定”行(空行)的形式表现出来。
- 使用聚合函数时,SELECT子句中只能存在以下三种元素。 ● 常数 ● 聚合函数 ● GROUP BY子句中指定的列名(也就是聚合键)
- 使用GROUP BY子句时,SELECT子句中不能出现聚合键之外的列名。
- 常见错误② ——在GROUP BY子句中写了列的别名
- SELECT子句在GROUP BY子句之后执行。在执行GROUP BY子句时,SELECT子句中定义的别名,DBMS还并不知道。
- 在GROUP BY子句中不能使用SELECT子句中定义的别名。
- GROUP BY子句结果的显示是无序的。
- 只有SELECT子句和HAVING子句(以及之后将要学到的ORDER BY子句)中能够使用COUNT等聚合函数。
- 只有SELECT子句和HAVING子句(以及ORDER BY子句)中能够使用聚合函数。
- 在“想要删除选择结果中的重复记录”时使用DISTINCT,在“想要计算汇总结果”时使用GROUP BY。
3-3 为聚合结果指定条件
- WHERE子句用来指定数据行的条件,HAVING子句用来指定分组的条件。
- WHERE子句只能指定记录(行)的条件,而不能用来指定组的条件
- HAVING子句中能够使用的3种要素如下所示。● 常数● 聚合函数● GROUP BY子句中指定的列名(即聚合键)
- WHERE子句=指定行所对应的条件 HAVING子句=指定组所对应的条件
- 聚合键所对应的条件不应该书写在HAVING子句当中,而应该书写在WHERE子句当中。
- 通过WHERE子句指定条件时,由于排序之前就对数据进行了过滤,因此能够减少排序的数据量。但HAVING子句是在排序之后才对数据进行分组的,因此与在WHERE子句中指定条件比起来,需要排序的数据量就会多得多。
3-4 对查询结果进行排序
- ▲子句的书写顺序 1. SELECT子句→2. FROM子句→3. WHERE子句→4. GROUP BY子句→5. HAVING子句→6. ORDER BY子句
- ASC和DESC是ascendent(上升的)和descendent(下降的)这两个单词的缩写。
- 未指定ORDER BY子句中排列顺序时会默认使用升序进行排列。
- 规则是优先使用左侧的键,如果该列存在相同值的话,再接着参考右侧的键。
- 使用含有NULL的列作为排序键时,NULL会在结果的开头或末尾汇总显示
- 排序键中包含NULL时,会在开头或末尾进行汇总。
- ▲使用HAVING子句时SELECT语句的顺序 FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY
- 一定要记住SELECT子句的执行顺序在GROUP BY子句之后,ORDER BY子句之前。
- ORDER BY子句中也可以使用存在于表中、但并不包含在SELECT子句之中的列
- 在ORDER BY子句中可以使用SELECT子句中未使用的列和聚合函数。
- 在ORDER BY子句中不要使用列编号。
4-1 数据的插入(INSERT语句的使用方法)
- 将列名和值用逗号隔开,分别括在()内,这种形式称为清单
- 原则上,执行一次INSERT语句会插入一行数据。
- INSERT语句中想给某一列赋予NULL值时,可以直接在VALUES子句的值清单中写入NULL。
- 省略INSERT语句中的列名,就会自动设定为该列的默认值(没有默认值时会设定为NULL)。
- INSERT语句的SELECT语句中,可以使用WHERE子句或者GROUP BY子句等任何SQL语法(但使用ORDER BY子句并不会产生任何效果)。
4-2 数据的删除(DELETE语句的使用方法)
- ① DROP TABLE语句可以将表完全删除② DELETE语句会留下表(容器),而删除表中的全部数据
- DELETE语句的删除对象并不是表或者列,而是记录(行)。
- 可以通过WHERE子句指定对象条件来删除部分数据。
- 与SELECT语句不同的是,DELETE语句中不能使用GROUP BY、HAVING和ORDER BY三类子句,而只能使用WHERE子句。
4-3 数据的更新(UPDATE语句的使用方法)
- SET子句中赋值表达式的右边不仅可以是单纯的值,还可以是包含列的表达式。
- 使用UPDATE语句可以将值清空为NULL(但只限于未设置NOT NULL约束的列)。
4-4 事务
- DBMS的事务具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)四种特性。通常将这四种特性的首字母结合起来,统称为ACID特性。
- 事务就是需要在同一个处理单元中执行的一系列更新处理的集合。
- 事务是需要在同一个处理单元中执行的一系列更新处理的集合。
- 使用事务开始语句和事务结束语句,将一系列DML语句(INSERT/UPDATE/DELETE语句)括起来,就实现了一个事务处理。
- COMMIT是提交事务包含的全部更新处理的结束指令(图4-3),相当于文件处理中的覆盖保存。一旦提交,就无法恢复到事务开始前的状态了。因此,在提交之前一定要确认是否真的需要进行这些更新。
- 原子性是指在事务结束时,其中所包含的更新处理要么全部执行,要么完全不执行,也就是要么占有一切要么一无所有。
- 一致性指的是事务中包含的处理要满足数据库提前设置的约束,如主键约束或者NOT NULL约束等。
- 隔离性指的是保证不同事务之间互不干扰的特性。
- 持久性也可以称为耐久性,指的是在事务(不论是提交还是回滚)结束后,DBMS能够保证该时间点的数据状态会被保存的特性。即使由于系统故障导致数据丢失,数据库也一定能通过某种手段进行恢复。
5-1 视图
- 第一点是由于视图无需保存数据,因此可以节省存储设备的容量。
- 表中存储的是实际数据,而视图中保存的是从表中取出数据所使用的SELECT语句。
- 第二个优点就是可以将频繁使用的SELECT语句保存成视图,这样就不用每次都重新书写了。
- 应该将经常使用的SELECT语句做成视图。
- 视图就是保存好的SELECT语句
- 应该避免在视图的基础上创建视图。
- 视图的限制① ——定义视图时不能使用ORDER BY子句
- 定义视图时不要使用ORDER BY子句。
- 视图的限制② ——对视图进行更新
- 视图归根结底还是从表派生出来的,因此,如果原表可以更新,那么视图中的数据也可以更新。反之亦然,如果视图发生了改变,而原表没有进行相应更新的话,就无法保证数据的一致性了。
- 视图和表需要同时进行更新,因此通过汇总得到的视图无法进行更新。
- 使用CASCADE选项来删除关联视图。
5-2 子查询
- 子查询的特点概括起来就是一张一次性视图。
- 子查询就是将用来定义视图的SELECT语句直接用于FROM子句当中
- 子查询作为内层查询会首先执行。
- 标量子查询就是返回单一值的子查询。
- 但是由于在WHERE子句中不能使用聚合函数,因此这样的SELECT语句是错误的。
- 能够使用常数或者列名的地方,无论是SELECT子句、GROUP BY子句、HAVING子句,还是ORDER BY子句,几乎所有的地方都可以使用。
5-3 关联子查询
- 在WHERE子句中使用子查询时,该子查询的结果必须是单一的。
- 在细分的组内进行比较时,需要使用关联子查询。
- 结合条件一定要写在子查询中
- 具体来讲,子查询内部设定的关联名称,只能在该子查询内部使用(图5-9)。换句话说,就是“内部可以看到外部,而外部看不到内部”。
6-1 各种各样的函数
- 聚合函数基本上只包含COUNT、SUM、AVG、MAX、MIN这5种
- 主流的DBMS都支持MOD函数,只有SQL Server不支持该函数。
- ||函数在SQL Server和MySQL中无法使用。
- SQL Server使用“+”运算符(函数)来连接字符串A。MySQL使用CONCAT函数来完成字符串的拼接。
- UPPER就是大写转换函数。
- CURRENT_DATE——当前日期
- EXTRACT——截取日期元素
- COALESCE是SQL特有的函数。该函数会返回可变参数[插图]中左侧开始第1个不是NULL的值。参数个数是可变的,因此可以根据需要无限增加。
6-2 谓词
- BETWEEN的特点就是结果中会包含100和1000这两个临界值
- 在使用IN和NOT IN时是无法选取出NULL数据的
- IN谓词(NOT IN谓词)具有其他谓词所没有的用法,那就是可以使用子查询作为其参数。
- EXIST通常都会使用关联子查询作为参数。
- 通常指定关联子查询作为EXIST的参数。
- 作为EXIST参数的子查询中经常会使用SELECT *。
6-3 CASE表达式
- 虽然CASE表达式中的ELSE子句可以省略,但还是希望大家不要省略。
- CASE表达式中的END不能省略。
7-1 表的加减法
- 集合运算符会除去重复的记录。
- 作为运算对象的记录的列数必须相同
- 可以使用任何SELECT语句,但ORDER BY子句只能在最后使用一次
- 在集合运算符中使用ALL选项,可以保留重复行。
- INTERSECT应用于两张表,选取出它们当中的公共记录。
7-2 联结(以列为单位对表进行联结)
- 所谓联结运算,一言以蔽之,就是“以A中的列作为桥梁,将B中满足同样条件的列汇集到同一结果之中”,具体过程如下所述。
- 进行联结时需要在FROM子句中使用多张表。
- 使用联结时SELECT子句中的列需要按照“<表的别名>.<列名>”的格式进行书写。
- 外联结要点① ——选取出单张表中全部的信息
- 内联结只能选取出同时存在于两张表中的数据
- 外联结中使用LEFT、RIGHT来指定主表。使用二者所得到的结果完全相同。
8-1 窗口函数
- PARTITION BY能够设定排序的对象范围。
- PARTITION BY在横向上对表进行分组,而ORDER BY决定了纵向排序的规则。
- 窗口函数兼具分组和排序两种功能。
- 通过PARTITION BY分组后的记录集合称为“窗口”。
- 由于专用窗口函数无需参数,因此通常括号中都是空的。
- 原则上窗口函数只能在SELECT子句中使用。
- 像这样以“自身记录(当前记录)”作为基准进行统计,就是将聚合函数当作窗口函数使用时的最大特征。
- OVER子句中的ORDER BY只是用来决定窗口函数按照什么样的顺序进行计算的,对结果的排列顺序并没有影响。
- 将聚合函数作为窗口函数使用时,会以当前记录为基准来决定汇总对象的记录。
8-2 GROUPING运算符
- 超级分组记录默认使用NULL作为聚合键。
- 使用GROUPING函数能够简单地分辨出原始数据中的NULL和超级分组记录中的NULL。
- 可以把CUBE理解为将使用聚合键进行切割的模块堆积成一个立方体。