关系数据库标准语言 SQL

Posted @了凡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关系数据库标准语言 SQL相关的知识,希望对你有一定的参考价值。

博主介绍:

我是了 凡 微信公众号【了凡银河系】期待你的关注。未来大家一起加油啊~


文章目录

3.1 SQL 概述

3.1.1 SQL 的产生与发展


目前,没有一个数据库系统能够支持SQL标准的所有概念和特性。大部分数据库系统能支持SQL/92标准中的大部分功能以及SQL99、SQL2003中的部分新概念。同时,许多软件厂商对SQL基本命令集还进行了不同程度的扩充和修改,又可以支持标准以外的一些功能特性

3.1.2 SQL的特点

  • 综合统一
  • 高度非过程化
  • 面向集合的操作方式
  • 以同一种语法结构提供多种使用方式
  • 语言简洁,易学易用

3.1.3 SQL的基本概念

支持SQL的关系数据库管理系统同样支持关系数据库三级模式结构。


其中外模式包括若干视图(view)和部分基本表(base table),模式包括若干基本表,内模式包括若干存储文件(stored file)。

3.2 学生-课程数据库

第一步就是先要建立一个课程数据库, 举例三张表

  • 学生表:Student(Sno,Sname,Ssex,Sage,Sdept)
  • 课程表:Course(Cno,Cname,Cpno,Ccredit)
  • 学生选课表:SC(Sno,Cno,Grade)

3.3 数据定义


一个关系数据库管理系统的实例(instance)中可以建立多个数据库,一个数据库中可以建立多个模式,一个模式下通常包括多个表、视图和索引等数据库对象。

3.3.1 模式的定义与删除

1.定义模式

在SQL中,模式定义语句如下:

Create schema <模式名>authorization<用户名>;
  • 如果没有指定<模式名>,那么<模式名>隐含为<用户名>。要创建模式,调用该命令的用户必须拥有数据库管理员权限,或者获得了数据库管理员授予的Create schema的权限。

  • 目前,在Create schema中可以接受create table,create view 和 grant子句。也就是说用户可以在创建模式的同时在这个模式定义中进一步创建基本表、视图,定义授权。

2.删除模式

在SQL中,删除模式语句如下:

drop schema<模式名><cascade|restrict>;

其中Cascade 和 restrict 两者必须选起义。选择了cascade(级联),表示在删除模式的同时把该模式中所有
的数据库对象全部删除;选择了restrict(显示)表示如果该模式中已经定义了下属的数据库对象(如表、视图
等),则拒绝该删除语句的执行。只有当该模式中没有任何下属的对象时才能执行DROP SCHEMA语句。

3.3.2 基本表的定义、删除与修改

1.定义基本表

SQL 语言使用 Create table语句定义基本表,其基本格式如下:

create table<表名> (<列名><数据类型>[列级完整性约束条件]
                    [,<列名><数据类型>[列级完整性约束条件]]
                    ...
                    [,<表级完整性约束条件>]
);

建表的同时通常还可以定义与该表有关的完整性约束条件,这些完整性约束条件被存入系统的数据字典中,当用户操作表中数据时由关系数据库管理系统自动检查该操作是否违背这些完整性约束条件。如果完整性约束条件涉及该表的多个属性列,则必须定义在表级上,否则可以定义在列级也可以定义在表级。

例如,建立一个“学生”表Student。

create table student()
    (Sno CHAR(9) PRIMARY KEY,
    Sname CHAR(20) UNIOUE,
    Ssex CHAR(2),
    Sage SMALLINT,
    Sdept CHAR(20)
    );

系统执行该CREATE TABLE语句后,就在数据库中建立一个新的空“学生”表Student,并将有关“学生”表的定义及有关约束条件存放在数据字典中。

2.数据类型

关系模型中一个很重要的概念是域。每一个属性来自一个域,它的取值必须是域中的值。

在SQL标准支持多种数据类型,以下列出常用几种数据类型。


3.模式与表

每一个基本表都属于某一个模式,一个面试包含多个基本表。当定义基本表时一般可以有三种方法定义它所属的模式。

例如,定义一个学生-课程模式S-T。现在要在S-T中定义Student、Cource、SC等基本表。

方法一,在表名中明显地给出模式名。

create table "S-T".Student(...);           /* Student 所属的模式是S-T*/
create table "S-T".Course(...);        -- Course所属的模式是 S-T
create table "S-T".SC(...);            -- SC 所属的模式是S-T

方法二,在创建模式语句中同时创建表
方法三,设置所属的模式,这样在创建表时表名中不必给出模式名。

当用户创建基本表(其他数据库对象也一样)时若没有指定模式,系统根据搜索路径(search path)来确定该对象所属的模式。

4.修改基本表

随着应用环境和应用需求的变换,有时需要修改已建立好的基本表。SQL 语言用ALTER RABLE语句修改基本表,其一般格式为

alter table<表名>
[ADD [COLUMN]<新列名><数据类型>[完整性约束]]
[ADD<表级完整性约束>]
[DROP[COLUMN]<列名i>[CASCADE|RESTRICT]]
[DROP CONSTRAINT<完整性约束名>[RESTRICT|CASCADE]]
[ALTER COLUMN<列名><数据类型>];

其中<表名>是要修改的基本表,ADD子句用于增加新列、新的列级完整性约束条件和新的表级完整性约束条件。DROP COLUMN 子句用于删除表中的列,如果指定了CASCADE短语,则自动删除引用了该列的其他对象,比如视图:如果指定了RESTRICT短语,则如果该列被其他对象引用,RDBMS将拒绝删除该列。DROP CONSTRAINT子句用于删除指定的完整性约束条件。ALTER COLUMN子句用于修改原有的列定义,包括修改列名和数据类型。

5.删除基本表

当某个基本表不再需要时,可以使用DROP TABLE语句删除它。其一般格式为:

drop table <表名>[RESTRICT|CASCADE];

若选择RESTRICT,则该表的删除是有限制条件的。删除的基本条件表不能被其他表的约束所引用(如 CHECK,FOREIGN KEY 等约束),不能有视图,不能有触发器(trigger),不能有存储过程或函数等。如果存在这些依赖该表的对象,则此表不能被删除。

3.3.3 索引的建立与删除

当表的数据量比较大时,查询操作会比较耗时。建立索引是加快查询速度的有效手段。数据库索引类似于图书后面的索引,能快速定位到需要查询的内容。用户可以根据应用环境的需要再基本表上建立一个或多个索引,以提供多种存取路径,加快查找速度。

数据库索引有多种类型,常见索引包括顺序文件上的索引、B+树索引、散列(hash)索引、位图索引等。

建立与删除索引由数据库管理员或表的属主(owner),即建立表的人,负责完成。关系数据库管理系统在执行查询时会自动选择合适的索引作为存取路径,用户不能显式地选择索引。

1.建立索引

建立索引使用CREATE INDEX语句,格式为:

create [UNIQUE][CLUSTER]INDEX<索引名>
ON<表名>(<列名>[<次序>][,<列名>[<次序>]]...);

其中,<表名>是建立索引的基本表的名字。索引可以建立在改表的一列或多列上,各列名之间用逗号分隔。每个<列名>后面还可以用<次序>指定索引值的排列次序,可选ASC(升序)或DESC(降序),默认值为ASC。

2.修改索引

对于已经建立的索引,如果需要对其重新命名,可以使用ALTER INDEX语句。格式为:

alter index<旧索引名>rename TO<新索引名>;

3.删除索引

索引一经建立就由系统使用和维护,不需用户干预。建立索引是为了减少查询操作的时间,但如果数据增、删、改频繁,系统会花费许多时间来维护索引,从而降低了查询效率。

删除索引使用DROP INDEX语句,格式为:

drop index<索引名>;

3.3.4 数据字典

数据字典是关系数据库管理系统内部的一组系统表,它记录了数据库中所有的定义信息,包括关系模式定义、视图定义、索引定义、完整性约束定义、各类用户对数据库的操作权限、统计信息等。

3.4 数据查询

数据查询是数据库的核心操作。SQL提供了SELECT语句进行数据查询,改语句具有灵活的使用方式和丰富的功能。

3.4.1 单表查询

单表查询是指仅涉及一个表的查询。

1.选择表中的若干列

选择表中的全部或部分列即关系代数的投影运算。

  • 查询指定列
    例如,查询全体学生的学号与姓名

    select Sno,Sname from Student;
    

    执行过程:从Student表中取出一个元组,取出改元组在属性Sno和Sname上的值,形成一个新的元组作为输出。对Student表中的所有元组做相同的处理,最后形成一个结果关系作为输出。

  • 查询全部列
    例如,查询全体学生详细记录

    select * from Student;
    

    等价于

    select Sno,Sname,Ssex,Sage,Sdept from Student;
    
  • 查询经过计算的值
    select 子句的<目标列表达式>不仅可以是表中的属性列,也可以是表达式
    例如,查询全体学生的姓名及其出生年份

    select Sname, 2014-Sage from Student;  --查询结果第2列是一个算术表达式
    

2.选择表中的若干元组

  • 消除取值重复的行
    两个本来并不完全相同的元组在投影到指定的某些列上后,可能会变成相同的行。可以用DISTINCT消除它们。

  • 查询满足条件的元组
    查询满足指定条件的元组可以通过WHERE子句实现。

3.ORDER BY子句

用户可以用ORDER BY子句对查询结果按照一个或多个属性列的升序(ASC)或降序(DESC)排列,默认为升序。

例如,查询选修了3号课程的学生的学号及其成绩,查询结果按分数的降序。

select Sno,Grade from SC where Cno='3' order by grade desc;

或相反,默认升序。

4.聚集函数

为了进一步方便用户,增强检索功能,SQL提供了许多聚集函数,主要有:

count(*)                         -- 统计元组个数
count([distinct|all]<列名>)       -- 统计一列中值的个数
sum([distinct|all]<列名>)       -- 计算一列值的总和(此列必须是数值型)
avg([distinct|all]<列名>)       -- 计算一列值的平均值(此列必须是数值型)
max([distinct|all]<列名>)       -- 求一列值中的最大值
min([distinct|all]<列名>)       -- 求一列值中的最小值

如果指定DISTINCT 短语,则表示在计算时要取消指定列中的重复值。如果不指定DISTINCT 短语或指定ALL 短语(ALL为默认值),则表示不取消重复值。

5.GROUP BY子句

GROUP BY子句将查询结果按某一列或多列的值分组,值相等的为一组。对查询结果分组的目的是为了细化聚集函数的作用对象。如果未对查询结果分组,聚集函数将作用于整个查询结果。分组后聚集函数将作用于每一组,即每一组都有一个函数值

例如,求各个课程号及相应的选课人数

select Cno,COUNT(Sno) from SC GROUP BY Cno;

查询结果假定为:

如果分组后还要求按一定的条件对这些组进行筛选,最终只输出满足指定条件的组,则可以使用HAVING短语指定筛选条件。

例如,查询选修了三门以上课程的学生学号。

select Sno from SC group by Sno having count(*)>3;

3.4.2 连接查询

若一个查询同时涉及两个以上的表,则称之为连接查询。连接查询是关系数据库中最主要的查询,包括等值连接查询、自然连接查询、非等值连接查询、自身连接拆线呢、外连接查询和复合条件连接查询等。

1.等值与非等值连接查询

连接查询的 where 子句中用来连接两个表的条件称为连接条件或连接谓词,其一般格式为

[<表名 1>.] <列名 1><比较运算符>[<表名 2>.]<列名 2>

其中比较运算符主要有=、>、<、>=、<=、!=(或<>)等。
此外连接谓词还可以使用下面形式;

[<表名 1>.]<列明 1>BETWEEN [<表名 2>.] <列名 2>AND[<表名2>.]<列名 3>

当连接运算符为=时,称为等值连接。使用其他运算符称为非等值连接连接谓词中的列名称为连接字段。连接条件中的各连接字段类型必须是可比的,但名字不必相同。

例如,查询每个学生及其选修课程的情况。

学生情况在Student表中,学生选课情况在SC表中,所以这次查询涉及了两张表。这两张表之间的联系是通过公共属性Sno实现的。

Select Student.*,SC.*  from Student,SC where Student.Sno = SC.Sno;  -- 将Student与SC中同一学生的元组连接起来

查询结果:

2.自身连接

连接操作不仅可以在两个表之间进行,也可以是一个表与其自己进行连接,称为表的自身连接。

例如,查询每一门课的间接先修课(即先修课的先修课)

比如说我要先学数据库但是学数据库之前就要先学数据结构,但是我要去学数据结构就要先学 PASCAL 语言。这就是间接先修课。

为此,要为 Course 表取两个别名,一个是FIRST,另一个是 SECOND。

完成该查询的 SQL 语句为

select FIRST.Cno,SECOND.Cpno 
from Course FIRST.Course SECOND 
where FIRST.Cpno=SECOND.Cno;

结果为:

3.外连接

假设有这样的两个表格:Student与 SC表 Student为所有的学生,而SC表为学生的选课表,我们可以知道王敏和张力没有选课,当使用Student.Sno=Sc.Sno的条件时,我们会发现无法出现王敏和张立的信息

为此,此时可以这样使用:

select Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
from Student left outer join sc on (Student.Sno=SC.Sno);

结果为:

4.多表连接

连接操作除了可以是两表连接、一个表与其自身连接外,还可以是两个以上的表进行连接,后者通常称为多表连接

例如,查询每个学生的学号、姓名、选修的课程名及成绩。本查询涉及三个表,完成该查询的SQL语句如下:

select Student,Sno,Sname,Cname,Grade
from Student,SC,Course
where Student.Sno=SC.Sno AND sc.Cno=Course.Cno;

关系数据库管理系统在执行多表连接时,通常是先进行两个表的连接操作,再将其连接结果与第三个表进行连接。

3.4.3 嵌套查询

在SQL 语言中,一个 SELECT-FROM-WHERE语句称为一个查询块嵌套在另一个查询块的WHERE 子句或 HAVING 短语的条件中的查询称为嵌套查询(nested query)。

例如:

select Sname       -- 外层查询或父查询
from Student
where Sno IN
            (select Sno    -- 内层查询或子查询
            from SC
            where Cno='2');

1.带有IN谓词的子查询

在嵌套查询中,子查询的结果往往是一个集合,所以谓词IN是嵌套查询中最经常使用的谓词。

例如,查询与“王林”同一个系学习的学生的所有信息

一般我们的思路是先拿到“王林”的系名,在从系里查找一起学习的学生的所有信息,所以需要两步骤:

  • 第一步:查找“王林”所在的系名,假设结果为C1

    select dept from dbo.s where sn='王林'
    
  • 第二步:查找一个系学习的学生的所有信息

    select * from dbo.s where dept='C1'
    

但是这样看明显很麻烦而且还要多次查询比较耗时占用资源,所以我们使用 IN 谓词嵌套查询:

select * from dbo.s where dept in(select dept from dbo.s where sn='王林')

2.带有比较运算符的子查询

带有比较运算符的子查询是指父查询与子查询之间用比较运算符进行连接。当用户能确切知道内层查询返回的是单个值时,可以用>、<、=、>=、<=、!=或<>等比较运算
符。

其使用方法和 IN 一致。
例如,由于一个学生可能在一个系学习,也就是说内查询的结果是一个值,因此可以用=代替 IN:

select Sno,Sname,Sdept
from Student
where Sdept =
        (select Sdept
        from Student
        where Sname='刘辰');

3.带有ANY(SOME)或ALL谓词的子查询

子查询返回单值时可以用比较运算符,但返回多值时要用ANY(有的系统用SOME)或ALL谓词修饰符。而使用ANY或ALL谓词时则必须同时使用比较运算符。其语义为:

> ANY 大于子查询结果中的某个值
> ALL  大于子查询结果中的所有值
< ANY 小于子查询结果中的某个值
< ALL 小于子查询结果中的所有值

>= ANY  大于等于子查询结果中的某个值
>= ALL  大于等于子查询结果中的所有值
<= ANY 小于等于子查询结果中的某个值
<= ALL 小于等于子查询结果中的所有值


= ANY 等于子查询结果中的某个值
= ALL 等于子查询结果中的所有值(通常没有实际意义)

!=(<>) ANY 不等于子查询结果中的某个值
!=(<>) ALL 不等于子查询结果中的任何一个值

例如,查询其他系中比计算机科学系某一学生年龄小的学生姓名和年龄

select Sname,Sage 
from Student 
where Sage < any(select Sage 
                from Student  
                where Sdept='CS') 
AND Sdept <> 'CS';

结果如下:

4.带有EXISTS谓词的子查询

exists谓词代表存在量词。带有exists谓词的子查询不返回任何数据,只产生逻辑真值“true”或逻辑假值“false”。可以利用exists来判断 属性与关系表之间的属于关系,关
系表之间的包含、相等关系,关系表之间是否存在交集。

话不多说,第一眼我也没看懂,来个例子看下也许就明白了。

例如,查询所有选修了 1 号课程的学生姓名。

思路:在Student中依次取每个元组的Sno值,用此值去检查 SC 表。若SC中存在这样的元组,其Sno值等于此Student.Sno值,并且其 Cno=‘1’,则取此Student.Sname送入结果表。SQL 语句是

select Sname
from Student
where exists
            (select *
            from SC
            where Sno=Student.Sno and Cno='1');

使用存在量词EXISTS后,若内层查询结果非空,则外层的WHERE 子句返回真值,否则返回假值。

3.4.4 集合查询

SELECT 语句的查询结果是元组的集合,所以多个SELECT 语句的结果可进行集合操作。集合操作主要包括并操作UNION、交操作 INTERSECT 和差操作 EXCEPT

这一小节非常有意思,主要就是集合的并集,交集,差集三个语法,能看到我的文章的同学一定知道数学里的三种意思。

话不多说,看个实例,查询计算机科学系的学生性别为男的学生。

首先我们要查找两个集合
一性别为男的集合

select * from Student where Ssex = '男'

二计算机科学系的学生

select * from Student where Sdept = '计算机科学系'

之后我们用 INTERSECT进行合并

select * from Student 
where Ssex = '男'
intersect
select * from Student 
where Sdept = '计算机科学系'

这样我们就能查到计算机科学系的学生性别为男的学生了。
结果为:

3.4.5 基于派生表的查询

子查询不仅可以出现在WHERE子句中,还可以出现在 FROM 子句中,这时子查询生成的临时派生素(derived table)成为主查询的查询对象。大眼一看我也不懂。举例我们分析一下

例如,找出每个学生超过他自己选修课程平均成绩的课程号。

SQL语句为:

select Sno,Cno 
from SC,(select Sno,avg(Grade) from SC group by Sno)
                        as Avg_sc(avg_sno,avg_grade)
where SC.Sno = Avg_sc.avg_sno and SC.Grade >= Avg_sc.avg_grade

通过 select Sno,avg(Grade) from SC group by Sno 我们可以获得每个学生的学号以及平均成绩并且通过 AS 命名为 Avg_sc。之后主查询将 SC 表与Avg_sc 按学号相等进行连接,选出选修课成绩大于其平均成绩的课程号。

3.5 数据更新

数据更新操作有三种:向表中添加若干行数据、修改表中的数据和删除表中的若干行数据。在SQL中有相应的三类语句。

3.5.1 插入数据

SQL 的数据插入语句 INSERT 通常有两种形式,一种是插入一个元组,另一种是插入子查询结果。后者可以一次插入多个元组。

1.插入元组

插入元组的 INSERT 语句格式为:

insert into<表名>[(<属性列 1>[<属性列 2>]...)]
values(<常量 1>[,<常量 2>]...);

这么看可能不是很懂,再看一个实例你就明白了

例如,将一个新学生元组(学号201215128,姓名:陈东,性别:男,所在系:计科系,年龄:18岁)插入到 Student 表中。

SQL 语句为:

insert 
into Student(Sno,Sname,Ssex,Sdept,Sage)
values('201215128','陈东','男','计科系',18);

2.插入子查询结果

这个设计非常方便,我们可以先从数据库查询一个集合,把这个查询语句当作插入的子查询,这样可以用查询出的结果映射在要插入的属性字段中。

插入子查询结果的 INSERT 语句格式为

insert into<表名>[(<属性列 1>[,<属性列 2>...])
子查询;

例如,对每一个系,求学生的平均年龄,并把结果存入数据库。

SQL语句为:

insert
into Dept_age(Sdept,Avg_age)

select Sdept,AVG(Sage)
from Student
group by Sdept;

3.5.2 修改数据

修改操作又称更新操作。我们用 UPDATE 和 SET 来进行修改操作。

1.修改某一个元组的值

例如,将学生201215121的年龄改为22岁。

update Student
set Sage=22
where Sno='201215121';

2.修改多个元组的值

例如,将所有学生的年龄增加1岁。

update Student
set Sage=Sage+1;

3.带子查询的修改语句

这种修改方式和插入操作一致也是嵌套的方式。

例如,将计算机科学系全体学生的成绩置零。

update SC
set Grade=0
where Sno in
            (select Sno 
            from Student
            where Sdept='计算机科学系');

3.5.3 删除数据

删除操作和插入修改基本相差不多,当然了既然是删除就要更为严谨一点。一般我们用 DELETE语句 进行删除操作,这里强调一点,DELETE 语句删除的是表中的数据,而不是表种的属性(字段)。

1.删除某一个元组的值

例如,删除学号为201215121的学生记录。

delete
from Student
where Sno='201215121';

2.删除多个元组的值

例如,删除所有的学生选课记录

delete
from SC;

这个语句可以使 SC 表成为空表,删除了SC的所有元组。

3.带子查询的删除语句

删除操作当然也可以使用嵌套方法进行指定删除。

例如,删除计算机科学系所有学生的选课记录

delete
from SC 
where Sno in
            (select Sno
            from Student
            where Sdept='计算机科学系');

3.6 空值的处理

空值定义:空值就是“不知道”或“不存在”或“无意义”的值

SQL 语言中允许某些元组的某些属性在一定情况下取空值。一般情况如下: