Table of Contents
第三章 SQL
3.1 SQL查询语言概览
3.2 SQL数据定义
3.2.1 基本类型
3.2.2 基本模式定义
3.3 SQL查询的基本结构
3.3.1 单关系查询
3.3.2 多关系查询
3.3.3 自然连接
3.4 附加的基本运算
3.4.1 更名运算 as
3.4.2 字符串运算
3.4.3 select 子句中的属性说明
3.4.4 排列元组的显示次序 order by
3.4.5 where子句谓词
3.5 集合运算
3.6 空值null
3.7 聚集函数
3.8 嵌套子查询
3.8.1 集合成员资格:in, not in
3.8.2 集合的比较
3.8.3 空关系测试:exists, not exists
3.8.4 重复元组存在性测试unique
3.8.5 from子句中的子查询
3.8.6 with子句
3.8.7 标量子查询scalar subquery
3.9 数据库的修改
总结:
SQL:结构化查询语言。最新的SQL标准是2008。
SQL语言包括以下几个部分:
SQL DDL不仅能够定义一组关系,还能定义每个关系的信息,包括:
每种类型都可能包括一个被称为空的特殊值。
注意:当比较char和varchar时,是否会自动在varchar类型后面追加空格取决于数据库的实现。
注意:SQL禁止破坏完整性约束的任何数据库更新。
create table R ( A1 D1 not null, A2 D2, ..., An Dn, <完整性约束1>, ..., <完整性约束2>);
insert into R (A1, A2, ..., An) values (V1, V2, ..., Vn); insert into R values (V1, V2,..., Vn);
delete from R; --从表R中删除全部元组 drop table R; --从数据库中删除关系R
alter table R add A D; --给表R添加属性A,其域为D alter table R drop A; --删除表R的属性A
SQL查询的基本结构由三个子句构成:select, from 和where。
-- 可以包括重复数据
select A1, A2,.., An from R;
-- 去除重复
select distinct A1, A2,.., An from R;
-- 显示的不去除重复
select all A1, A2, ..., An from R;
-- select子句可以含有+-*/运算符的算术表达式;运算对象可以是常数或元组的属性。
select A1, A2, ..., A3 * 1.1 from R;
-- where子句可以包括逻辑连词and, or 和not
-- 逻辑连词的运算对象可以使包含比较运算符的<, <=, >, >=, = 和<>的表达式
select A1, A2, ... An from R where A1=V1 and A2=V2;
SQL查询的基本结构由三个子句构成:select, from 和where子句构成:
执行顺序:from->where->select。
在SQL的实际实现中,它会通过尽可能只产生满足where子句谓词的笛卡尔积元素来进行优化执行。如果省略where子句,则会输出笛卡尔积。
笛卡尔积:它将第一个关系的每个元组与第二个关系的所有元组都进行连接。
自然连接:只考虑连接在两个关系上都出现的属性上取值相同的元组对。
-- 在R1和R2上都有的属性做连接
select A1, A2,...,An from R1 natrual join R2;
-- 在仅在A1和A2属性连接R1和R2 - 自然连接的一种形式
-- 相当于 select A1, A2,...,An from R1, R2 where R1.A1=R2.A1 and R1.A2 = R2.A2;
select A1, A2,...,An from R1 join R2 using (A1, A2);
例如:
select name, course_id from instructor, teaches where instructor.ID = teaches.ID;
--可以简写为:
select name, course_id from instructor natural join teaches;
oldname as newname
-- 找出满足下面条件的所有教师的姓名,他们的工资至少比Biology系的某一个教师的工资要高
select distinct T.name from instructor as T, instructor as B where T.salary>S.salary and S.dept_name = 'Biology';
/*其中T和S 被称作相关名称(correlation name)/表别名(table alias)/相关变量(correlation variable)/元组变量(tuple variable)*/
字符串上可以使用的常见函数:
在字符串上使用like操作符实现模式匹配:
模式是大小写敏感的。
'Intro%' - 匹配“Intro”打头的字符串
'%Comp%' - 匹配任何包括“Comp”子串的字符串
'___' - 匹配只含三个字符的字符串
'___%' - 匹配至少包含三个字符的字符串
// 可以使用escape关键字来定义转义符 like 'ab\%cd%' escape
'\' - 匹配以“ab%cd”开头的字符串
select R.* from R;
order by 默认使用升序排列,asc表示升序,desc表示降序
select * from R order by A1; select * from instructor order by salary desc, name asc;
select name from instructor where salary between 90000 and 100000;
select name, course_id from instructor, teaches where instructor.ID= teaches.ID and dept_name = "Biology";
--可以重写为:
select name, course_id from instructor, teaches where (instructor.ID, dept_name) = (teaches.ID, 'Biology');
(select course_id from section where semester = "Fall" and year = 2019) union (select course_id from section where semester = "Spring" and year = 2020)
(select course_id from section where semester = "Fall" and year = 2019) intersect (select course_id from section where semester = "Spring" and year = 2020)
(select course_id from section where semester = "Fall" and year = 2019) except (select course_id from section where semester = "Spring" and year = 2020)
空值运算规则:
扩展到unknown的布尔运算:
注意:
select distinct会去除重复元组。在该过程中,需要比较两个元组的对应的属性值。如果两个值都是非空并且值相等,或者都为空,那么它们是相同的。
但是这与谓词对待空值的方式不同,谓词中null = null 会返回unknown,而不是true。
聚集函数是以值的一个集合为输入,返回单个值的函数。
包含having和group by 子句的查询的执行顺序:
对空值null进行聚集的原则:除了count(*)外,所有的聚集函数都会忽略输入集合中的空值
SQL提供嵌套子查询机制。子查询是嵌套在另一个查询中的select-from-where表达式。
in: 测试元组是否是集合中的成员
not in: 测试元组是否不是集合中的成员
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)
注意:= some 等价于 in, <>some不等价于not in
select name from instructor where salary > some (select salary from instructor where dept_name = 'Biology');
注意:<>all 等价于not in, =al不等价于in
select name from instructor where salary > all (select salary from instructor where dept_name = 'Biology');
exists: 测试一个子查询的结果中是否存在元素
not exists: 测试一个子查询的结果中是否不存在元素
unique:用于测试一个子查询的结果中是否含有重复元组。
not unique: 用于测试在一个子查询结果中是否存在重复元组
-- 找出所有在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)
-- 找出所有在2009年最少开过两次的课程
select T.course_id from course as T where not unique (select R.course_id from section as R where T.course_id = R.course_id and R.year = 2009)
注意:有的数据库可以支持对嵌套子查询的进行属性重命名。但是oracle不支持。
select dept_name, avg_salary from (select dept_name, avg(salary) from instructor group by dept_name) as dept_avg(dept_name,avg_salary) where avg_salary > 42000;
from子句中的嵌套子查询不能使用凯子from子句中其他关系的相关变量,但是SQL 2003允许from子句中的子查询使用关键词lateral最为前缀,以便访问from子句中在它前面的表或子查询中的属性。
-- 打印每位教师的姓名,工资以及他们所在的系的平均工资
select name, salary, avg_salary from instructor I1, lateral (select avg(salary) as avg_salary from instructor I2 where I2.dept_name = I1.dept_name);
with子句提供定义临时关系的方法,这个定义只对包含with子句的查询有效。
-- 找出具有最大预算值的系
with max_budget(value) as (select max (budget) from department) select budget from department, max_budget where department.budget = max_budget.value;
若子查询返回单个属性的单个元组,则该子查询称为标量子查询。标量子查询可以出现select-from-where子句以及having子句中。
-- 列出所有的系,即它们所拥有的教师数
select dept_name, (select count(*) from instructor where department.dept_name = instructor.dept_name) as num_instructors from department;
delete from r where P;
insert into r values (v1, v2, ..., vn);
-- 在执行insert前,先执行完select语句
insert into r (select (A1, A2, ..., An) from r where P);
update r set A1 = v1 where P;
-- case结构
case when pred1 then res1 when pred2 then res2 ... when predn then resn else res0 end
/* 更新每个student的tot_cred属性值设为该生成功 学完的课程的总学分。grade既不是F也不是null,则表明成功学完了该门课程*/
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.grode is not null );
/*如果学生没有成功完成任何一门课程,则tot_cred被设置为null。 如果希望将这样的属性值设为0,那么需要使用另一条update */
-- ===>上述sql可改写为
update student S set tot_cred = ( select case when sum(credits) is not null then sum(credits) else 0 end from takes natural join course
where S.ID = takes.ID and takes.grade<>'F' and takes.grode is not null);