SQL基础教程(第2版)

MICK

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理解为将使用聚合键进行切割的模块堆积成一个立方体。