《数据库系统概念》第三章


第三章 SQL

SQL(结构化查询语言)是使用最广泛的数据库查询语言。

尽管我们说 SQL 语言是种”查询语言“,但除了数据库查询,它还具有很多其他功能:定义数据结构、修改数据库数据、说明安全性约束条件等。

我们将介绍 SQL 的基本结构和概念,SQL 的各种实现可能在一些细节上有所不同,或只支持整个语言的一个子集。

3.1 SQL 查询语言概览

SQL语言有??部分

  1. 数据定义语言 DDL:SQL DDL 提供定义、删除、修改关系模式的命令。
  2. 数据操纵语言 DML:SQL DML 提供从数据库中查询信息,及插入、删除、修改元组的能力。
  3. 完整性:SQL DDL 包含定义完整性约束的命令。
    • 保存在数据库中的数据必须满足所定义的完整性约束。破坏完整性约束的更新时不允许的。
  4. 视图定义:SQL DDL 包含定义视图的命令。
  5. 事务控制:SQL 包含定义事务的开始和结束的命令。
  6. 嵌入式 SQL 和动态 SQL:嵌入式和动态 SQL 定义 SQL 语句如何嵌入到编程语言,如 C、C++、Java中。
  7. 授权:SQL DDL 包含定义对关系和视图的访问权限的命令。

本章我们给出对 SQL 的基本 DML 和 DDL 特征的概述。

在此描述的特征自 SQL-92 以来就一直是 SQL 标准的部分。

3.2 SQL 数据定义

数据库中的关系集合必须由数据定义语言(DDL)指定给系统。

DDL 不仅能定义一组关系,还能定义每个关系的信息,包括:

  1. 每个关系的模式
  2. 每个属性的取值类型
  3. 完整性约束
  4. 每个关系维护的索引集合
  5. 每个关系的安全性和权限信息
  6. 每个关系在磁盘上的物理存储结构

本章只讨论基本模式定义和基本类型,SQL DDL 的其他特征将在第4、5章讨论。

3.2.1 基本类型

SQL 标准支持多种固定类型:

  1. char(n)固定长度的字符串,用户指定长度 \(n\) 。也可使用全称 character
  2. varchar(n)可变长度的字符串,用户指定最大长度 \(n\) 。等价于全称 character varying
  3. int整数类型(和机器相关的整数的有限子集)。等价于全称 integer
  4. smallint整数类型(和机器相关的整数类型的子集)。
  5. numeric(p, d)定点数,精度由用户指定 —— 有 \(p\) 位数字(加上一个符号位)其中 \(d\) 位数字在小数点右边。
  6. realdouble precision:浮点数与双精度浮点数,精度与机器有关。
  7. float(n):精度至少为 \(n\) 位的浮点数。

每种类型都包含一个特殊值 —— 空值:表示一个缺失的值,该值可能不存在或未知。

SQL 提供 nvarchar 类型存放使用 Unicode 表示的多语言数据。但很多数据库允许在 varchar 类型中存放 Unicode(用UTF_8表示)

  1. char 数据类型存放固定长度的字符串。

?? 属性 A 的类型为 char(10),若我们为 A 属性存入字符串”Avi“,则该字符串后会追加7个空格使其达到10个字符的长度;反之,若属性 B 的类型为varchar(10),存入”Avi“,则不会增加空格。

  1. 比较两个 char 类型的值时,若二者长度不同,则在比较之前会自动在短值后增加额外空格使二者长度一致。

  2. 比较 char 类型和 varchar 类型时,是否会在 varchar 类型后增加空格使二者长度一致,取决于数据库系统。故建议始终使用 varchar 类型而不是 char 类型以避免此问题。

3.2.2 基本模式定义

  1. 定义 SQL 关系: create table

    create table r
    ( A1 D1,
      A2 D2,
      ...,
      An Dn,
      <完整性约束1>,
      ...,
      <完整性约束k>);
    
    • \(r\) 是关系名;\(A_i\) 是关系 \(r\) 模式中的一个属性名,\(D_i\) 是属性 \(A_i\) 的域;

      也就是说 \(D_i\) 指定了属性 \(A_i\) 的类型以及可选的约束,用于限制所允许的 \(A_i\) 取值的集合。

    • 完整性约束:

      • primary key\((A_{j1},A_{j2},...,A_{jm})\)

        声明属性 \(A_{j1},A_{j2},...,A_{jm}\) 构成关系的主码。主码属性必须非空且唯一。

      • foreign key\((A_{k1},A_{k2},...,A_{kn})\) references \(r\)

        声明关系中任意元组在属性 \(A_{k1},A_{k2},...,A_{kn}\) 上的取值必须对应于关系 \(r\) 中某些元组在主码属性上的取值。

      • not null

        一个属性上的 not null 约束表明该属性上不允许空值。

    create table instructor
    (ID        varchar(5),
     name      varchar(20) not null,
     dept_name varchar(20),
     salary    numeric(8,2),
     primary key(ID),
     foreign key(dept_name) reference department);
    
  2. 删除 SQL 关系: drop table

    drop table r;
    
  3. 修改 SQL关系: alter table

alter table r add A D;  -- 为已有关系 r 增加属性,A 是待添加的属性名,D 是待添加属性的域
alter table r drop A;   -- 删除已有关系 r 中的属性 A
  1. 将数据加载到关系中: insert
insert into instructor
values(10211, 'Smith', 'Biology', 66000);
  1. 从关系中删除所有元组:命令 delete
delete from r;           -- 删除关系 r 中的所有元组
delete from r where P;   -- 删除关系 r 中满足 P 条件的元组

命令以分号结束,但在很多 SQL 实现中分号可选。

3.3 SQL 查询的基本结构

SQL 查询的基本结构由子句 select、from、where 组成。

from 子句列出的关系是查询的输入,在这些关系上进行 whereselect 子句中指定的运算,然后产生一个关系作为结果。

3.3.1 单关系查询

“找出所有教师所在的系名”:select dept_name from instructor;

  1. SQL允许在关系以及SQL表达式结果中出现重复,若要删除重复,可在 select 后加入关键词 distinct

    • 默认保留重复元组,也可使用关键词 all 显式指明不去除重复。
  2. select 子句可包含 算术表达式(含 +、-、*、/ 运算符),运算对象可为常数或元组的属性。

    select ID, name, dept_name, salary*1.1 from instructor;

  3. 通过where子句选择满足特定谓词的元组,where子句中可使用逻辑连词 and、or、not

    • 逻辑连词的运算对象可以是包含比较运算符(<、>、<=、>=、=、<>)的表达式。
  • 还可使用 between 比较运算符说明一个值>=某个值,且<=某个值。同理,还有 not between 比较运算符。

     找出所有在Computer Science系且工资在7000到10000美元的教师姓名: 
    select name from instructor where dept_name = 'Comp.Sci.' and salary between 7000 and 10000;

    SQL 允许我们使用比较运算符来比较字符串、算术表达式、特殊类型(如日期类型) 

3.3.2 多关系查询

  1. 涉及多个关系的SQL查询中,各子句作用:

    • select 子句:列出查询结果中所需要的属性
    • from 子句:列出查询求值中需要访问的关系
    • where 子句:列出作用在 from 子句中关系的属性上的谓词

     select 子句中,若有多个属性名相同,需使用关系名作为前缀加以区分。

  2. 理解查询所代表的的运算:

    1. from 子句中列出的关系产生笛卡尔积
    2. 在步骤1的结果上应用 where 子句中指定的谓词
    3. 对步骤2结果中的每个元组,输出 select 子句中指定的属性(或表达式的结果)

     上述步骤的顺序有助于理解SQL查询的结果应该是怎样的,而不是此结果怎样被执行的。

     SQL 的实际实现中不会执行这种形式的查询,它尽可能只产生满足 where 子句谓词的笛卡尔积元素,以此优化执行。

  3. 星号“*” 可在 select 子句中表示“所有的属性”。

找出所有教师姓名,及他们所在系的名称和系所在建筑的名称: 
select name, instructor.dept_name, building
from instructor, department
where instructor.dept_name = department.dept_name;

3.3.3 自然连接

  1. 自然连接natural join 作用于两个关系,并产生一个关系作为结果。只考虑 在两个关系模式中共同属性上取值相同的元组对。

    ?? 两个关系中可能存在多个共同属性,此时自然连接的结果为共同属性取值相同的元组对。

    对大学中所有讲授课程的教师,找出他们的姓名及讲述的的所有课程标识:
    select name, course_id from instructor, teachers where instructor.ID = teachers.ID
    select name, course_id from instructor natural join teachers
    若instructor表与teachers表只有ID这个共同属性,则上述二种写法等价,若还有其他共同属性,则两种写法不等价。

  2. from 子句中,可用自然连接将多个关系结合在一起作为一个新的关系。

    select A1, A2, ..., An
    from r1 natural join r2 natural join r3, r4
    where P;
    
  3. SQL 也提供了一种自然连接的构造形式,允许用户指定需要哪些列相等—— join···using 运算,需给定一个共同属性名列表。

    select name, course_id from instructor join teachers using (ID)

3.4 附加的基本运算

3.4.1 更名运算

  1. 之前查询结果中的属性名为 from 子句中关系的属性名。可通过 as 子句重命名结果关系中的属性。

    • as 子句可出现在 select 子句中,重命名属性
    • as 子句可出现在 from 子句中,重命名关系
  2. 当需要比较同一关系中元组的情况时,重名名关系是必须的。

    找出满足下面条件的所有教师的姓名,他们的工资至少比 Biology系某一个教师的工资要高: 
    select distinct T.name 
    from instructor as T, instructor as S
    where T.salary > S.salary and S.dept_name = 'Biology';

  3. 相关名称/表别称/相关变量/元祖变量:被用于重命名关系的标识符。 ??的 T 和 S

3.4.2 字符串运算

  1. 字符串用 单引号标识,用两个单引号标识字符串中原本的单引号字符。字符串 'it''s right' 的含义是 it's right

  2. SQL 标准中 字符串匹配大小写敏感表达式“ 'comp.sci.' = 'Comp.Sci.' ”结果为假。但一些数据库如 MySQL、SQL Server中,不区分大小写。但这种默认方式可在数据库级或特定属性级被修改。

  3. SQL 中由很多操作字符串的函数:

  • upper(s) 将字符串 s 转换为大写;lower(s) 将字符串 s 转换为小写
  • trim(s) 去除字符串 s 后面的空格
  • substring(s,first,last) 选取s的子串
  1. 使用 like 操作符实现字符串的模式匹配,使用 escape 关键词定义转义字符;使用 not like 比较运算符搜寻不匹配项。

    • 百分号(%):匹配任意子串
    • 下划线(_):匹配任意一个字符

    like 'ab%cd%' escape '\'    匹配所有以“ab%cd”开头的字符串。

找出所有建筑名称中包含子串 ’Watson‘ 的所有系名:
select dept_name
from department
where building like '%Watson%';

3.4.3 排列元组的显示次序

order by 子句:让查询结果的元组按排列顺序显示。

  • 使用 desc 表示降序,asc 表示升序,默认使用升序。
  • 排序可在多个属性上进行。

按工资的降序列出整个instructor关系,若教师工资相同则按姓名升序排列:
select *
from instructor
order by salary desc, name asc;

3.4.4 where 子句谓词

SQL 允许使用记号 (v1, v2, ..., vn)表示一个分量值分别为 v1, v2, ..., vn 的n维元组。在元组上可使用比较运算符,将按 字典顺序 进行比较运算。

(a1, a2) <= (b1, b2) 在 a1<=b1 且 a2<=b2 时为真

查找Biology系讲授了课程的所有教师的姓名和他们所讲授的课程:
select name, course_id from instructor, teacher where instructor.ID = teacher.ID and dept_name = 'Biology';
select name, course_id from instructor, teacher where (instructor.ID, dept_name) = (teacher.ID, 'Biology') ;
二者写法等价。

3.5 集合运算

SQL 作用在关系上的 unionintersectexpect 运算对应于数学集合论中的 \(\cup、\cap、-\)

3.5.1 并运算

查找在2009年秋季开课,或在2010年春季开课,或在两个学期都开课的所有课程:
select course_id from section where semester = 'Fall' and year = 2009 )
union
select course_id from section where semester = 'Spring' and year = 2010 )

  1. union 运算自动去除重复。
  2. 若想保留重复,必须使用 union all 替代 union。结果中重复元组数 = 关系c1 和 c2 中出现的重复元组数之和。

3.5.2 交运算

将??的 union 改为 intersect,则可查找在2009年秋季和2010年春季同时开课的所有课程。

  1. intersect 运算自动去除重复。
  2. 若想保留重复,必须使用 intersect all 替代 intersect。结果中重复元组数 = 关系c1 和 c2 中出现的重复次数最少的那个。

3.5.3 差运算

将??的 union 改为 expect,则可查找在2009年秋季开课,且不在2010年春季开课的所有课程。

  1. expect 运算自动去除重复。
  2. 若想保留重复,必须使用 expect all 替代 expect。结果中重复元组数 = c1中出现的重复元组数 - c2中出现的重复元组数(前提是此差为正)。

3.6 空值

空值会给关系运算带来特殊问题,包括算术运算、比较运算、集合运算。

  1. 使用 is nullis not null 可判断表达式结果是否为空值。
  2. 算术表达式 的任一输入为空 null,则该算术表达式(涉及诸如 +、-、*、/)结果为空 null
  3. 涉及空值的任何 比较运算 的结果为 unknown
  • 这是除 truefalse 外的第三个逻辑值。
  • 可使用 is unknownis not unknown 判断表达式结果是否为 unknown
  1. where 子句中的 逻辑谓词 and、or、not 可对比较结果进行布尔运算,这些布尔运算的定义也被扩展到可处理 unknown 值。
  • true and unknown 结果为 unknownfalse and unknown 结果为 false
  • true or unknown 结果为 truefalse or unknown 结果为 unknown
  • not unknown 结果为 unknown
  1. 当查询使用 select distinct 子句时,会去除重复元组——元组在所有属性上的取值都相等即视为重复元组,即使某些值为空。
    • 这种对待空值的方式,与谓词中对待空值的方式不同,谓词中 “null=null” 会返回 unknown,而不是 true
    • 这也适用于 集合 的并、交、差运算。

3.7 聚集函数

  1. 聚集函数:以值的一个集合(集或多重集)为输入、返回单个值的函数。

  2. SQL中提供的聚集函数:

    • 平均值:avg
    • 最小值:min
    • 最大值:max
    • 总和:sum
    • 计数:count

    avg、sum 的输入必须为数字集合,其他可作用在非数字集合上,如字符串。

3.7.1 基本聚集

找出 2010年春季学期讲授一门课程的教师总数:
select count(distinct ID) as teachers_count 
from teachers
where semester = 'Spring' and year = 2010 ;

  1. 若结果关系中的某属性由聚集产生,则数据库会给她一个任意的名字,我们可使用 as 子句给它重命名。
  2. 有时在计算聚集函数前需使用 distinct 去除重复值,有时不用 比如计算平均值时
    • count(*) 可计算一个关系中的元组个数,但使用 count(*) 时不能使用 distinct
    • 使用 max、min 时可使用 distinct,但结果不会有差别。

3.7.2 分组聚集

将聚集函数作用在一组元组集上。

  1. 通过 group by 子句中给出的一个/多个属性构造分组,属性上取值都相同的元组被分为一组。
    • 不写 group by 子句时,可将整个结果视作一个分组。
    • select 子句中只能有被聚集的属性和出现在 group by 子句中的属性。
  2. having 子句可设置分组的限定条件。
    • having 子句中的谓词在形成分组后才能起作用。
    • having 子句中只能有被聚集的属性和出现在 group by 子句中的属性。
  3. 包含聚集、group byhaving 子句的查询的含义理解:
    • 根据 from 子句计算出一个关系。可理解为 from 中列出的关系做笛卡尔积
    • where 子句中的谓词应用到 from 子句的结果关系上。
    • 若出现 group by 子句,将满足 where 谓词的元组通过 group by 子句形成分组;若无 group by 子句,则将满足 where 谓词的元组视为一个分组。
    • 若出现 having 子句,将它应用到每个分组上,不满足 having 子句谓词的分组将被舍弃。
    • select 子句利用剩下的分组产生查询结果的元组(在每个分组上应用聚集函数来得到单个元组)。

对于在2009年讲授的每个课程段,若该课程段有至少2名学生选课,找出选修该课程段的学生的总学分的平均值:
select course_id, sec_id, semester, year, avg(tot_cred)
from takes natural join student
where year = 2009
group by course_id, sec_id, semester, year
having count(ID) >=2 ;

3.7.3 对空值和布尔值的聚集

  1. 除了 count(*) 外,所有的聚集函数都忽略输入集合中的空值。
  2. 空集count 运算值为0,其余聚集运算返回一个空值。

3.8 嵌套子查询

  1. 子查询:是嵌套在另一个查询的 where 子句中的 select-from-where 表达式。
  2. 子查询常用于对集合的成员资格、集合的比较以及集合的计数进行检查。

3.8.1 集合成员资格

  1. 连接词 in 测试元组是否是集合中的成员,连接词 not in 测试元组是否不是集合中的成员。

    • 集合由 select 子句产生的一组值构成(子查询)

      找出在2009年秋季和2010年春季学期同时开课的所有课程:
      select distinct course_id
      from section
      where semester = 'Fall' and year = 2009 and course_id in (select course_id
      from section
      where semester = 'Spring' and year = 2010 ) ;

      此查询也可由交运算完成。

    • 集合也可由 枚举 构成

      找出既不叫 “Mozart”又不叫“Einstein”的教师姓名:
      select distinct name
      from instructor
      where name not in ('Mozart', 'Einstein') ;

  2. ??的例子中都是在单属性关系中测试成员资格,也可测试任意关系的成员资格。

    找出选修了ID为10101教师教授课程的学生总数:
    select count(distinct ID)
    from takes
    where (course_id, sec_id, semester, year) in (select course_id, sec_id, semester, year
    from teachers
    where teachers.ID = 10101 ) ;

3.8.2 集合的比较

some关键词 含义 all关键词 含义
< some 至少小于某一个 < all 小于所有
<= some 至少小于等于某一个 <= all 小于等于所有
> some 至少大于某一个 > all 大于所有
>= some 至少大于等于某一个 >= all 大于等于所有
= some 至少等于某一个 = all 等于所有
<> some 至少不等于某一个 <> all 不等于所有

= some 等价于 in,<> some 不等价于 not in<> all 等价于 not in,= all 不等价于 in

找出满足下面条件的所有教师的姓名,他们的工资至少比 Biology 系某一个教师的工资要高:
select distinct T.name
from instructor as T, instructor as S
where T.salary > S.salary and S.dept_name = 'Biology';

等价于:

select name
from instructor
where salary > some (select salary
from instructor
where dept_name = 'Biology';
子查询产生 Biology 系所有教师的所有工资值集合。当元组的salary值至少比 Biology 系教师的所有工资值集合中某一成员高时,外层 selectwhere 子句中 >some 的比较为真。

3.8.3 空关系测试

  1. SQL 可测试一个子查询的结果中是否存在元组。

    • exists 结构测试子查询结果集中是否存在元组,子查询非空时返回 true可用于两个条件同时发生的查询

      找出在2009年秋季和2010年春季学期同时开课的所有课程,也可写为:
      select distinct course_id
      from section as S
      where semester = 'Fall' and year = 2009 and exist (select *
      from section as T
      where semester = 'Spring' and year = 2010 and S.course_id = T.course_id) ;

    • not exists 结构测试子查询结果集中是否不存在元组,子查询为空时返回 true

    • “关系 A 包含关系 B”可写成“not exists( B except A)”。

      找出选修了Biology系开设的所有课程的学生(也就是找到这样的学生,他们不存在Biology系开设的课程没有选修的):
      select S.ID, S.name
      from student as S
      where not exists (( select course_id
      from course

      where dept_name = 'Biology')
      except
      (select T.course_id
      from takes as T

      where S.ID = T.ID) ;

      第一个子查询找出Biology系开设的所有课程集合;第二个子查询找出S.ID选修的所有课程。外层 select 对每个学生测试其选修的所有课程集合是否包含Biology系开设的所有课程集合。

  2. 相关子查询:使用了来自外层查询相关名称的子查询。

3.8.4 重复元组存在性测试

SQL 可测试一个子查询的结果是否存在重复元组。

  • unique 结构测试子查询结果中是否不存在重复元组。子查询没有重复元组时返回 true

    找出所有在2009年最多开设一次的课程:(意思是也有可能没开设)
    select T.course_id
    from course as T
    where unique (select R.course_id
    from section as R
    where T.course_id = R.course_id and R.year = 2009) ;

    在不使用 unique 结构的情况下,等价于:

    select T.course_id
    from course as T
    where 1>= (select count (R.course_id)
    from section as R
    where T.course_id = R.course_id and R.year = 2009) ;

  • not unique 结构测试子查询结果中是否存在重复元组。子查询有重复元组时返回 true

    将??例子中的 unique 改为 not unique 则表示找出所有在2009年最少开设两次的课程。

?? unique谓词在空集上计算为 true。 此结构尚未被广泛实现???♀?。

3.8.5 from子句中的子查询

  1. SQL可在 from 子句中使用子查询表达式。
    • 因为任何 select-from-where 表达式返回的结果都是关系,故可以被插入到另一个 select-from-where 中任何关系可出现的位置
  2. from 子句嵌套的子查询的结果属性可在外层查询中使用。
  3. from 子句嵌套的子查询不能使用来自 from 子句其他关系的相关变量。
  4. 可使用 as 子句给子查询的结果关系起个名字,并对属性进行重命名。

找出所在系中工资总额最大的系:(在此无法使用 having 子句实现查询)
select max(total_salary)
from (select dept_name, sum(salary)
            from instructor
            group by dept_name) as dept_total(dept_name, tot_salary)

?? 并非全部 SQL 实现都支持在 from 子句中嵌套子查询。

某些 SQL 实现要求对每个子查询结果关系都给一个名字,即使该名字从不被引用。

Oracle允许对子查询结果关系命名(省略掉 as 关键字)但不允许对关系中的属性重命名。

3.8.6 with子句

with 子句提供定义临时关系的方法,此定义只对包含 with 子句的查询有效。

  • 使用 with 子句能使查询在逻辑上更加清晰
  • 它还允许在一个查询内的多个地方使用视图定义。

查找所有工资总额大于所有系平均工资总额的系:
with dept_total(dept_name, value) as
         (selelct dept_name, sum(salary)
          from instructor group by dept_name)
         dept_total_avg(value) as
         (select avg(value) from dept_total)
select dept_name from dept_total, dept_total_avg
where dept_total.value >= dept_total_avg.value ;

?? 并非全部 SQL 实现都支持 with 子句。

3.8.7 标量子查询

  1. SQL 允许子查询出现在返回单个值的表达式能够出现的任何地方,只要该子查询只返回包含单个属性的但个元组。——这样的子查询称为 标量子查询只返回含单个属性的单个元组。
    • 技术上,标量子查询的结果类型仍是关系,尽管其中只包含单个元组。
    • 在表达式中使用标量子查询时,它出现的位置是单个值出现的地方,则SQL就从该关系中包含单属性的单元组中取出相应的值并返回。
    • 在编译时并非总能判断一个子查询返回的结果是否含多个元组;若在子查询被执行后其结果中有多个元组,则会产生运行时错误
  2. 标量子查询可出现在 selectwherehaving 子句中。

列出所有的系以及它们拥有的教师数:此例也说明了标量子查询中对相关变量(外层查询的 from 子句中关系的属性)的使用
select dept_name,
         (selelct count(*) from instructor 
          where department.dept_name = instructor.dept_name) 
         as num_instructors 
from department ;

3.9 数据库的修改

3.9.1 删除

  1. 删除操作只能删除整个元组,无法只删除元组某些属性上的值。
  2. delete 命令只能作用于一个关系。
    • 一次只能从一个关系中删除元组,但通过在 where 子句中嵌套 select-from-where 就可引用任意数目的关系。
delete from r where P ;  -- 先从 r 中找出所有使 P 为真的元组,然后将其从 r 中删除。若省略 where 子句,则删除 r 中所有元组

删除工资低于大学平均工资的教师记录:
delete from instructor
where salary < (select avg(salary) from instructor) ;

3.9.2 插入

  1. 待插入元组的属性值必须在相应属性的域中,待插入元组的分量数也必须正确。

  2. 待插入的元组可能只给出了模式中部分属性的值,则剩余属性会被赋为空值 null,表示值未知。

  3. SQL 允许在 insert 语句中指定属性(因此可不用考虑属性的排列顺序)。

insert into course(course_id, title,dept_name, credits) values ('CS-437', 'Database Systems', 'Comp.Sci', 4) ;

  1. 可在查询结果的基础上插入元组。

    insert into instructor
    ? select ID, name, dept_name, 18000 from student
    ? where dept_name = 'Music' and tot_cred > 144 ;

  2. 在执行插入之前先执行完 select 语句非常重要,若在执行 select 语句的同时执行插入:

    insert into student
    ? select * from student ;

    若student上没有主码约束,则??请求可能会插入无数元组。若在插入前先完成 select 语句就可避免此问题。

    若无主码约束,此请求会重新插入 student 中的第一个元组,产生该元组的第二份拷贝;由于此副本现在是student中的一部分,select 语句可能找到它,则第三份拷贝被插入到student中,如此无限循环。

3.9.3 更新

  1. 更新可在不改变整个元组的情况下改变其部分属性的值;SQL首先检查关系中的所有元组,看它们是否应该被更新,然后执行更新。

    给工资低于平均数的教师涨5%工资:
    update instructor
    set salary = salary * 1.05
    where salary < (select avg (salary) from instructor) ;

  2. update 语句中的 where 子句可以包含 select 语句的 where 子句中的任何合法结构(包括嵌套的 select)。

  3. insertdelete 语句类似,update 语句中嵌套的 select 可引用待更新的关系。

  4. SQL 提供 case 结构,可在一条 update 语句中执行两种更新,且避免更新次序引发的问题。

    给工资超过 100000美元的教师涨3%工资,其余教师涨5%:
    update instructor
    set salary = case
    ? where salary <= 100000 then salary * 1.05
    ? else salary * 1.03
    end

    case 语句的一般格式 如下:

    case
    	when pred1 then result1
    	when pred2 then result2
    	···
    	when predn then resultn
    	else result0
    end
    
  5. 标量子查询可用在 SQL 更新语句的 set 子句中。

    把每个student元组的tot_cred属性值设为该生成功学完的课程(成绩既不是F也不是空)学分的总和。
    update student S
    set tot_cred = (
    select sum (credits) from takes natural join course
    where S.ID = takes.ID and takes.grade <> 'F' and takes.grade is not null) ;

总结

image-20220326034233314

术语回顾

image-20220326034317118