关系数据库标准语言SQL

关系数据库标准语言SQL

1.SQL概述

SQL发展历程
SQL语言最早称为Sequel,是Boyce和Chamberlin在1974年提出的。1975-1979年IBM公司的San Jose研究实验室研制的关系数据库管理系统原型系统System R实现了这种语言。此后,Sequel不断发展,并更名为SQL。

SQL的功能

  • SQL的数据定义语言(DDL)提供了模式定义、修改和删除,基本表定义、修改和删除、域定义修改和删除、视图的定义和删除
  • SQL的数据操纵语言( DML)提供了数据查询子语言
  • DML还提供数据更新(数据插入、删除和修改)语句,允许用户更新数据
  • SQL DML允许用户对视图进行查询和受限的更新操作
  • SQL的数据控制语言(DCL)定义用户对数据对象的访问权限和审计

SQL的特点

  • 集多种数据库语言于一体。 SQL语言集数据定义语言(DDL ),数据操纵语言(DML ) ,数据控制语言( DCL)功能于一体。可以独立完成数据管理的核心操作。用户数据库投入运行后,可根据需要随时逐步修改模式,不影响数据的运行。数据操作符统一,查找、插入、删除、更新等只需一种操作符。

  • 高度非过程化。 非关系数据模型的数据操纵语言“面向过程”,必须使用类似于指针的机制,指定存取路径。SQL只要提出“做什么”,无须了解存取路径。系统将考察多种执行方案,选择并运行一个最优的执行方案得到结果。大大减轻了用户负担。有利于提高数据的独立性。

--查询职称(Title)为讲师的全体教师的姓名和性别
SELECT Tname,SEX
FROM Teachers
WHERE Title = '讲师';
  • 面向集合的操作方式 非关系数据模型采用面向记录的操作方式,操作对象是一条记录
    SQL采用集合操作方式。操作对象、查找结果可以是元组的集合。一次插入、删除、更新操作的对象可以是元组的集合。

  • 一种语法两种使用方式 SQL是独立的语言
    能够独立地用于联机交互的使用方式SQL又是嵌入式语言。SQL能够嵌入到高级语言(例如C,C++,Java)程序中,供程序员设计程序时使用。

  • 功能强大,语言简洁 SQL是一种完整地数据库语言,其功能涵盖数据定义、数据操纵、数据控制等数据管理的主要需求。但SQL语言相对比较简洁,其核心动词只有9个:CREATE,ALTER,DROP,SELECT,INSERT,DELETE,UPDATE,GRANT,REVOKE。SQL语言的语法简单,与英语口语的风格类似,易学易用。

2.数据类型

  • CHAR(n):定义字符串,长度n由用户指定。省略n时,长度为1,CHAR的全称是CHARACTER
  • VARCHAR(n):变长字符串,最大长度n由用户指定,VARCHAR的全称是CHARACTER VARYING
  • 定长和变长字符串的差别主要表现再前者需要固定长度的空间,而后者占用的空间在最大长度范围内是看改变的
  • BIT(n):定长二进位串,长度n由用户指定。省略n时长度为1
  • BIT VARYING(n):变长二进位串,最大长度n由用户指定
  • INT:整数,其值域依赖于具体实现。INT的全称时INTEGER
  • SMALLINT:小整数。其值域依赖于具体实现,但小于INT的值域
  • DEC(p,d):p位有效数字的定点数,其中小数点右边占d位。DEC的全称时DECIMAL
  • FLOAT(n):精度至少为n位的数字的浮点数,其值域依赖于实现
  • REAL:实数,精度依赖于实现
  • DOUBLE PRECISION:双精度实数,精度依赖于实现,但精度比REAL高
  • DATA:日期,包括年、月、日,格式位YYYY-MM-DD
  • TIME:时间,包括时、分、秒,格式位HH-MM-SS。TIME(n)可以表示比秒更小的单位,秒后取n位
  • TIMESTAMP:时间戳,时DATA和TIME的结合
  • INTERVAL:时间间隔。SQL允许对DATA、TIME和INTERVAL类型的值进行计算
  • SQL提供ETRACT(field FROM Var),Var可以时DATE、TIME或TIMESTAMP数据类型的变量,ETRACT函数的功能时从VAR中提取字段field
  • 例如如果d是DATE类型,则ETRACT(YEAR DROM d)返回d中的年份

3.模式(Schema)的定义和删除

关系的定位:

  • DBMS为关系的命名提供了一个三级层次结构。顶层由目录(catalog)组成,每个目录中包含一些模式(schema),而SQL对象(关系、视图等)都包含在模式内
  • 一个关系由目录名、模式名和关系名唯一确定,例如:Catalog2.Supply-schema.Suppliers

SQL环境:
目录、模式是为每一个连接建立的SQL环境的一部分,SQL环境还包括用户身份。
注意:SQL环境中的目录不能重名,同一目录下的模式不能重名,统一模式下的关系不能重名。

符号约定:

  • < X > 表示X是需要进一步定义或说明语言成分
  • [ X ] 表示X可以缺少或者出现一次
  • { X } 表示X可以出现一次
  • X|Y表示或者X出现,或者Y出现,但二者不能同时出现
  • SQL语言的保留字(如CREATA)不区分大小写。为醒目起见,对于SQL语句中的SQL保留字,我们使用大写

模式的定义:
1.CREATE SCHEMA <模式名>[<模式元素>...]
创建一个以<模式名>命名的模式,并可以在创建模式的同时为该模式创建或不创建模式元素。<模式元素>可以是表定义、视图定义、断言定义、授权定义等,这种格式没有授权其他用户访问创建的模式,以后可以用授权语句授权
2.CREATE SCHEMA [ <模式名> ] AUTHORIZATION <用户名> [<模式元素>…]
与第一中的区别在于它将船舰的模式授权予<用户名>指定的用户,党<模式名>缺省时,用<用户名>作为模式名

例子:为WangQiang创建一个名为Supply_schema的模式,可以用:

  • CREATA SCHEMA Supply_schema AUTHORIZATION WangQiang;
  • 所创建的模式在当前目录下,并为WangQiang所拥有
  • 如果用CREATE SCHEMA Supply_schema; 则创建一个名为Supply_schema的模式,但未向任何用户授权
  • 如果用CREATE SCHEMA AUTHORIZATION WangQiang; 则为WangQiang创建一个模式,并用WangQiang命名
--还可以在创建模式的同时创建该模式的对象
CREATE SCHEMA Supply_schema
	CREATE TABLE Suppliers
		(Sno	CHAR(5) PRIMERY KEY,
		Sname	CHAR(5) NOT NULL,
		Status	SMALLINT,
		Address	CHAR(30),
		Phone	CHAR(10));
--在创建模式Supply_schema的同时还在该模式中定义了一个基本表Suppliers

模式删除:
DBA和模式的拥有者可以用DROP SCHEMA删除模式。删除模式的语句格式为:

  • DROP SCHEMA <模式名> CASCADE | RESTRICT
    其中CASCADE和RESTRICT两者必须选择其一
  • CASCADE,则删除<模式名>指定模式得同时并删除该模式中的所有数据库对象(基本表、视图、断言等)
  • RESTRICT,则仅当<模式名>指定的模式不包含任何数据库对象时才删除指定的模式,否则拒绝删除

例子:

  • DROP SCHEMA Supply_schema RESTRICT
  • 仅当模式Supply_schema中不包含任何数据库对象时,才删除模式Supply_schema,否则什么也不做,而
  • DROP SCHEMA Supply_schema CASCADE
  • 将直接删除模式Supply_schema,并同时删除该模式中所有的数据库对象

4.表的定义和删除

//表的定义
CREATE TABLE <表名>
	(<列名><数据类型>[DEFAULT<缺省名>][列级约束定义],
	<列名><数据类型>[DEFAULT<缺省名>][列级约束定义],
	...,
	[<表级约束定义>,...,<表级约束定义>]);

表的定义:

  • 可选短语"DEFAULT<缺省值>"定义列上的缺省值,<缺省值>是<类型>中的一个特定值或空值(NULL)
  • 列级约束定义和该列相关的完整性约束
  • 表级约束定义和一个列或者多个列同时相关的完整性约束

列级约束的定义:[CONSTRAINT<约束名>]<列约束>

  • NOT NULL:不予许该列取空值
  • PRIMARY:指明该列是主码
  • UNIQUE:该列上的值必须唯一
  • CHECK(<条件>):指明该列的值必须满足的条件,其中<条件>是一个设计该列的布尔表达式

表级约束定义:[CONSTRAINT<约束名>]<表约束>

  • PRIMARY KEY(A1,…,Ak):说明属性列A1,…,Ak构成该关系的主码
  • UNIQUE(A1,…,Ak):说明属性列A1,…,Ak上的值必须唯一,这相当于说明A1,…,Ak构成该关系的候选码
  • CHECK(<条件>):说明该表上的一个完整性约束条件
  • FOREIGN KEY(A1,…,Ak)REFERENCES<外表名>(<外表主码>)[<参照触发条件>]
--例子:下面的语句创建教师表Teachers
CREATE TABLE Teachers
(Tno	CHAR(7) PRIMARY KEY,
Tname	CHAR(10)NOT NULL,
Sex		CHAR(2)CHECK(Sex='男' OR Sex='女'),
Birthday DATA,
Title	CHAR(6),
Dno		CHAR(4),
FOREIGN KEY (Dno)  REFERENCES Departments(Dno));

CREATE TABLE Departments
(Dno	CHAR(4) PRIMARY KEY,
Dname	CHAR(10),
Dheadno	CHAR(7),
);

--创建选课表CS用如下语句:
CREATE TABLE SC
(Sno		CHAR(9),
Cno			CHAR(5),
Grade		SMALLINT CHECK(Grade>=0 AND Grade<=100),
PRIMARY KEY (Sno,Cno),
FOREIGN KEY (Sno) REFERENCES Students(Sno),
FOREIGN KEY (Cno) REFERENCES Courses(CNO));

修改基本表:

ALTER TABLE<表名>
[ADD[COLUMN]<列名><数据类型>[列级约束定义]]
[ALTER[COLUMN]<列名>{SET DEFAULT<缺省值>|DROP DEFAULT}]
[DROP[COLUMN]<列名>{CASCADE|RESTRICT}]
[ADD<表约束定义>]
[DROP CONSTRAINT<约束名>{CASCADE|RESTRICT}]

--在Courses中增加一个新列Pno,表示课程的先行课的课程号
ALTER TABLE Courses ADD Pno CHAR (5);
--在Students的sex列设置缺省值“女”可以减少大约一半学生性别的输入
ALTER TABLE Students ALTER Sex SET DEFAULT‘女;
--删除Sex上的缺省值可以用
ALTER TABLE Students ALTER Sex DROP DEFAULT;
--删除Courses中的Pno列可以用
ALTER TABLE Courses DROP Pno;

删除基本表:

  • DROP TABLE<表名> {CASCADE|RESTRICT}
  • 其中CASCADE表示及联删除,依赖于表的数据对象(最常见的是视图)也将一同被删除
  • RESTRICT表示受限删除,如果基于该表定义有视图,或者有其他表引用该表(如CHECK、FOREIGN KEY等约束),或者该表有触发器、存储过程或函数等,则不能删除
  • 如果用如下语句删除SC表
    DROP TABLE SC RESTRICT;
  • 仅当没有依赖于SC的任何数据库对象删除才能成功。然而,如果用
    DROP TABLE SC CASCADE;

5.索引的创建和删除

索引的定义CREATE [UNIQUE] [CLUSTER] INDEX <索引名> ON <表名>(<列名>[<次序>],{<列名>|[<次序>]})

  • <索引名>为建立的索引命名

  • <表名>是要建立索引的基本表的名字

  • 索引哭建在该表的一列或多列上,各列用逗号分隔;每个<列名>后可以用<次序>指定索引值的排列次序

  • 次序可以是ASC(升序)和DESC(降序),缺省值为ASC

  • UNIQUE 表示该索引为唯一性索引UNIQUE缺省时,创建的索引为非唯一性索引

  • CLUSTER表示建立的索引是聚簇索引,缺省时为非聚簇索引

  • 创建索引不仅创建索引结构,而且将索引的定义存储在数据字典中

--例子:再Students的Dno上创建一个名为Students_Deopt的索引
	CREATE INDEX Student_Dept ON Students(Dno);
--再Teachers上的Dno创建一个名为Teacher_Dept的聚簇索引
	CREATE CLUSTER INDEX Teacher_Dept ON Teachers(Dno)
--注意:学生流动性比较大,Students更新频繁,不适合创建聚簇索引;而教师相对稳定,可以考虑按所在院系再Teachers上创建聚簇索引

索引的删除
索引一旦建立,就由系统来选择和维护,无需用户干扰,但党删除一些不必要的索引时,看用下列语句来实现
DROP INDEX <索引名>
删除索引时,系统将删除索引结构,并同时从数据字典中删去有关该索引的定义

--删除索引Students_Dept
	DROP INDEX Student_Dept;

6.SELECT语句介绍

--SELECT 语句的一般形式如下:
SELECT[ALL|DISTINCT]<选择序列>
FROM<表引用>,...,<表引用>
[WHERE <查询条件>]
[GROUP BY<分组列>{,<分组列>}[HAVING<分组选择条件>]]
[ORDER BY<排序列>[ASC|DESC]{,<排序列>[ASC|DESC]}]
--最基本的结构时SELECT-FROM-WHERE,并且SELECT子语句和FROM子句时必须的,其他子句都是可选的
//例子
--查询每个学生选修的每门课程的成绩,要求列出学号、姓名、课程名和成绩
	SELECT Student.Sno,Sname,Cname,Grade
	FROM Students, SC,Courses
	WHERE Syudents.Sno = SC.Sno AND SC.Cno = Courses.Cno;

7.简单查询

最简单的SELECT语句是只包括SELECT和FROM子句。这种语句只能完成对单个表的投影运算

--例子:查询所有课程的信息可以用
SELECT Cno,Cname,Period,Credit
FROM Courses;
--或者简单的用:
SELECT *
FROM Courses;

//SELECT 子句中的列可以是表达式
--假设我们定义了一个函数year(d),它返回DATE类型的参数D中的年份呢。下面的语句将显示学生的不同年龄:
	SELECT DISTINCT 2019 - year(Birthday) AS Age
	FROM Students;
//注意:DISTINCT短语的作用范围是所有目标列,
--例如,查询选修课程的各种成绩正确写法
	SELECT DISTINCT Cno,Grade
	FROM SC;
--错误的写法:
	SELECT DISTINCT Cno,DISTINCT Grade
	FROM SC;

复杂查询都需要使用WHERE子句说明查询条件

WHERE子句的常用形式

表达式形式调用
比较表达式< <= > >= = <>或 !=
BETWEEN表达式BETWEEN AND,NOT BETWEEN AND
IN表达式IN,NOT IN
LIKE表达式LIKE,NOT LIKE
NULL表达式IS NULL, IS NOT NULL

比较表达式常见形式:

  • <值表达式1>θ<值表达式2>
  • 其中θ是比较运算符(< <= > >= = <>或 !=),<值表达式1>和<值表达式2>都是可求值的表达式,并且他们的值可以进行比较。通常,这些值表达式是常量、属性和函数。比较表达式根据比较关系是否成立产生真假值
--查询信息工程学院的全体老师的信息
	SELECT *
	FROM Departments
	WHERE Dname = '信息工程学院'

BETWEEN表达式
判定一个给定的值是否在给定的闭区间,其最常见形式是:

  • <值表达式>[NOT]BETWEEN<下界>AND<上界>
  • 其中<值表达式>、<下界>和<上界>都是可求值的表达式,其值是序数类型
  • <下界>的值小于或等于<上界>
  • 当且仅当<值表达式>的值在<下界>和<上界>确定的闭区间时,<值表达式>BETWEEN<下界>AND<上界>为真,而<值表达式>NOT BETWEEN<下界>AND<上界>为假
--例子:查询出生年份在1997~1999年之间的学生的姓名和专业
	SELECT Sname,Speciality
	FROM Students
	WHERE year(Birthday) BETWEEN 1997 AND 1999;
--查询出生不在1997~1999年之间的学生的姓名和专业
	SELECT Sname,Speciality
	FROM Students
	WHERE year(Birthday) NOT BETWEEN 1997 AND 1999;

IN表达式

  • 版顶一个给定的袁术是否在给定的集合中
  • IN表达式有两种形式:1.<值表达式>[NOT]IN(<值表达式列表>) 2.<元组>|[NOT]IN<子查询>
  • 在第一种形式中,<值表达式>时可求值的表达似乎(同产生过hi属性),而<值表达式列表>包括一个或多个可求值的表达式(通常是字面值,如45,‘教授’等),中间用都好隔开
  • 当且仅当<值表达式>的值出现在<值表达式列表>中,<值表达式>IN(<值表达式列表>)为真,而<值表达式>NOT IN(<值表达式列表>)为假
--例子:查询计算机科学与技术和软件工程专业的学生的学号和姓名
	SELECT Sno,Sname
	FROM Students
	WHEERW Speciality IN('计算机科学与技术','软件工程')
--例子:查询既不是计算机科学与技术,也不是软件工程专业的学生的学号和姓名
	SELECT Sno,Sname
	FROM Students
	WHEERW Speciality NOT IN('计算机科学与技术','软件工程')

LIKE表达式

  • LIKE表达式允许进行模糊查询
  • LIKE表达式的一般形式为: [NOT]LIKE<匹配串>[ESCAPE'<换码字符>']
  • 其中,<匹配串>是给定的字符串常量,允许使用通配符。有两种通配符:1. “_”(下划线)可以玉任意单个字符匹配 2 . “%”可以与零个或多个任意字符匹配
  • ESCAPE’<换码字符>'用于定义转移自度,将紧随其后的一个字符转义
--例子:查询所有以“数据”开头的课程
	SELECT Cname
	FROM Courses
	WHERE Cname LIKE '数据%';
--查询姓李并且姓名只有两个汉字的学生学号和姓名
	SELECT Sno,Sname
	FROM Students
	WHEERE Sname LIKE '李__';
//注意:一个汉字占两个字符位置

--查询以C_大头的课程的详细信息
--由于通配符“_”出现在模式中,我们需要使用转义字符将它转义。该查询可以使用如下语句实现:
	SELECT *
	FROM Courses
	WHERE Cname LIKE 'C\_%'ESCAPE'\';
--其中,ESCAPE短语定义“\”为转义字符,模式’C\_%’中的“_”被转义,不在取通配符含义,而是取字面意义
//注意: ’C\_%’中的“%”仍然是通配符,因为转义字符支队紧随其后的一个字符转义

NULL表达式

  • SQL语序元组的某些属性上取空值(NULL)。空值代表未知的值,不能与其他值进行比较
  • NULL表达式允许我们判定给定的值是否为空值。NULL表达式的常见形式: <值表达式>|<子查询>IS[NOT]NULL
  • 通常,<值表达式>是属性
  • 例如,如果A是属性,则A IS NULL为真当且仅当属性A上取空值
--查询成绩为空的学生的学号和课程号
	SELECT Sno,Cno
	FROM SC
	WHERE Grade IS NULL;

8.排序和分组

ORDER BY子句可以将查询的结果按一定次序显示,其形式如下:ORDER BY <排序列>[ASC|DESC]{,<排序列>[ASC|DESC]}

  • 其中,<排序列>是必须出现在SELECT子句中的属性名或属性的别名
  • ORDER BY后可以有一个或多个<排序列>,中间用逗号隔开
  • 每个<排序列>都可以独立指定按升序(ASC)还是按降序(DESC)排序,缺省时为升序
  • 如果指定多个<排序列>,则查询结果按指定的次序,首先按第一个<排序列>的值排序,第一个<排排序列>值相同的结果元组按第二个<排序列>的值排序,如此下去
//例子
--查询每位同学选修CS202课程的成绩,并将查询结果按成绩降序排序
	SELECT*
	FROM SC
	WHERE Cno = 'CS202'
	ORDER BY Grade DESC;

--查询每位学生的没门课程的成绩,并将查询结果按课程号升序、成绩降序排序
	SELECT*
	FROM SC
	ORDER BY Cno Grade DESC;

聚集函数
SQL语言提供了一些聚集函数,如下表所示。使用聚集函数可以方便的进行各种统计查询

聚集函数名含义
COUNT([ALLDISTINCT] *)
COUNT([ALLDISTINCT] 列名)
SUM([ALLDISTINCT]列名)
AVG([ALLDISTINCT]列名)
MAX([ALLDISTINCT]列名)
MIN([ALLDISTINCT]列名)
//例子
--查询CS102课程成绩最低分、平均分、最高分
	SELECT MIN(Grade),AVG(Grade),MAX(Grade)
	FROM SC
	WHERE Cno = 'CS102';
--查询选修了CS102课程的学生的人数
	SEKECT COUNT(*)
	FROM SC
	WHERE Cno = 'CS102';

SC
分组语句group by

  • SQL语言提供了GROUP BY子句,其一般形式如下:GROUP BY<分组列>{,<分组列>}[HAVING<分组选择条件>]
  • 其中,<分组列>时属性(可以带表名前缀)。他所在的表出现在FROM子句中
  • 可选的HAVING短语用来过滤掉不满足<分组选择条件>的分组,缺省时等价与HAVING TRUE
  • <分组选择条件>类似于WHERE子句的查询条件,但其中允许出现聚集函数
//例子
--查询每门课程的平均成绩,输出课程号和平均成绩
	SELECT Cno ,AVG(Grade)
	FROM SC
	GROUP BY Cno;
--查询每个学生的平均成绩,并输出平均成绩大于85的学生学号和平均成绩
	SELECT Sno,AVG(Grade)
	FROM SC
	GROUP BY Sno HAVING AVG (Grade)>85;
	/*
	错误样例
	SELECT Sno,AVG(Grade)
	FROM SC
	WHERE AVG (Grade)>85
	GROUP BY Sno;
	*/

分组和聚集函数的关系
分组语句细化了聚集函数的作用范围
注意:对于带GROUP BY子句的SELECT语句,SELECT子句中的结果列必须时GROUP BY子句中的<分组列>或聚集函数

//例子
--查询每个学生的平均成绩,并输出平均成绩大于85的学生学号、姓名、平均成绩
	SELECT Students.Sno,Sname,AVG(Grade)
	FROM SC,Students
	WHERE Students.Sno = SC.Sno  
	GROUP BY Students.Sno,Sname HAVING AVG(Grade)>85;

9.连接查询

问题引入:查询林艳选修的数据库系统原理课程的成绩?
连接查询

  • 当查询需要的信息或者查询条件涉及的属性分布在多个表中时,需要进行连接查询
  • SQL支持连接查询,允许FROM子句中包括多个表
  • 当FROM子句中包含多个表时,相当于求这些表的笛卡尔积
  • 可以在WHERE子句中说明连接条件,并通过SELECT子句选取所需要的属性来实现各种连接
//例子
--查询学号为201905001的学生的各科成绩,对每门课程显示课程名和成绩
	SELECT Cname,Grade
	FROM SC,Courses
	WHERE SC.Cno = Courses.Cno AND Sno = '201905001';

表内容
输出

//例子
--查询每个学生的平均成绩,并输出平均成绩大于85的学生学号、姓名、平均成绩
	SELECT Students.Sno,Sname,AVG(Grade)
	FROM SC,Students
	WHERE Students.Sno=SC.Sno
	GROUP BY Students.Sno,Sname HAVING AVG(Grade)>85;
--查询和林艳专业相同的学生的姓名
	SELECT S2.Sname
	FROM Students S1,Students S2
	WHERE S1.Speciality=S2.Speciality
	AND S1.Sname = '林艳'
	AND S2.Sname<>'林艳';

10.嵌套查询

  • SQL是一种结构化查询语言,它允许将一个查询作为子查询嵌套在另一个SELECT语句中
  • 最常见的嵌套是将子查询嵌套在WHERE子句或HAVING短语的条件中
  • 称将一个查询嵌套在另一个查询中的查询称为嵌套查询,并称前者为子查询(内层查询)。后者为父查询(外层循环)
//例子
	SELECT	Cno,Cname
	FROM	Courses
	WHERE	Cno IN
				(SELECT	Cno
				FROM	SC
				WHERE	Sno='201705001')

IN引出的子查询

  • IN表达式的第二种形式可以更一般的判定集合成员资格,其形式如下:<元组>[NOT]IN<子查询>
  • 其中<元组>形如(<值表达式>,…,<值表达式>),并且当元组只有一个分量时,可以省略圆括号。当且仅当<元组>出现在<子查询>的结果中,<元组>IN<子查询>为真。而<元组>NOT IN<子查询>为假
--查询和林艳在同一个专业学习的女同学的学号和姓名
	SELECT	Sno,Sname
	FROM	Students
	WHERE	Sex = '女' AND Speciality IN
			(SELECT Speciality
			FROM Students
			WHERE Sname = '林艳')

集合的比较引出的子查询

  • SQL允许将一个元素与子查询的结果进行比较。这种量化比较表达式的常用形式是:<值表达式> θ ALL | SOME | ANY <子查询>
  • 其中<值表达式>通常是属性,θ是比较运算符(= <> > < != >= <=)。SOME和ANY的含义相同。早期只有ANY,但容易与英语中的any一次在语言上混淆,现在更多的使用SOME。当<子查询>的结果为单个值时,ALL、SOME和ANY可以省略

ALL和SOME

  • 设v是<值表达式>的值,S是<子查询>的查询结果。它是元组(值)的集合
  • v θ ALL S为值,当且仅当v和S中的每个值都满足比较关系θ
  • v θ SOME S(或 v θ ANY S)为真,当且仅当v与S中的某个值满足比较关系θ
//例子
--查询软件工程专业所有学生都小的其他专业的学生的学号、姓名、专业和出生日期
	SELECT Sno,Sname,Speciality,year(Birthday)
	FROM Students
	WHERE Speciality <>'软件工程' AND
	year(Birthday)> ALL (SELECT year(Birthday)
						FROM Students
						WHERE Speciality='软件工程')

注意:
用聚集函数实现子查询通常比直接用SOME或ALL查询效率要高。SOME和ALL与集函数的对应关系如下表所示
1

//例子
--查询软件工程专业所有学生都小的其他专业的学生的学号、姓名、专业和出生日期
	SELECT Sno,Sname,Speciality,year(Birthday)
	FROM Students
	WHERE Speciality <>'软件工程' AND
	year(Birthday)> ALL (SELECT MAX(year(Birthday))
						FROM Students
						WHERE Speciality='软件工程')

存在量词引出的子查询

  • 一般形式如下:[NOT] EXISTS<子查询>
  • EXISTS<子查询>为真,当且仅当<子查询>的结果非空(至少包含一个元组),NOT EXISTS刚好相反
  • <子查询>的SELECT子句的形式为:SELECT*
//例子
--查询所有选修了CS102课程的学生的学号和姓名
	SELECT Sno,Sname
	FROM Students,SC
	WHERE Students.Sno=SC.Sno AND Cno='CS102';
--也可以使用IN一道的嵌套查询来实现:
	SELECT Sno,Sname
	FROM Students
	WHERE Sno IN
		(SELECT Sno
		FROM SC
		WHERE Cno='CS102');
--该查询可以用如下语句实现:
	SELECT Sno,Sname
	FROM Students S
	WHERE EXISTS
	(SELECT*
	FROM SC
	WHERE Sno=S.Sno AND Cno='CS102');
//注意:不相关子查询的子查询的条件不依赖于父查询 
//相关子查询的子查询的条件以来父查询

--查询选修了全部课程的学生的学号和姓名
	SELECT Sno,Sname
	FROM Students S
	WHERE NOT EXISTS
		(SELECT*
		FROM Coursrs C
		WHERE NOT EXISTS
			(SELECT*
			FROM SC
			WHRER SC.Sno=S.Sno AND SC.Cno=C.Cno));

--查询至少选修了学号为201815122的学生选修的全部课程的学生的学号和姓名
	SELECT Sno,Sname
	FROM Students S
	WHERE NOT EXISTS
		(SELECT*
		FROM SC SC1
		WHERE SC1.Sno = '201815122'AND
			NOT EXISTS
			(SELECT *
			FROM SC SC2
			WHERE SC2.Sno=S.Sno AND SC2.Cno=SC1.Cno));

11.集合查询

  • SQL语言也支持传统的集合运算。包括并(UNION)、交(INTERSECT)、差(EXCEPT)。集合运算的常见形式为:<元组集表达式><远足集表达式>[ALL]<远足集表达式>
  • 其中<元组集表达式>产生元组的集合,通常是SELECT查询或集合运算的结果
  • 集合运算将自动删除结果中的重复元素。可选的ALL可以用来保留运算结果中的重复元组
//例子
--查询选修了CS301号课程到货选修了CS306号课程的学生的学号
	SELECT Sno
	FROM CS
	WHERE Cno='CS301'
	UNION
	SELECT Sno
	FROM SC
	WHERE Cno='CS306';
--等价于
	SELECT DISTINCT Sno
	FROM SC 
	WHERE Cno='CS301'OR Cno='CS306';


--查询既选修了CS301号课程又选修了CS306号课程的学生学号
	SELECT Sno
	FROM SC
	WHERE Cno='CS301' INTERSECT
		SELECT Sno
		FROM SC
		WHERE Cno='CS306'
--等价于
	SELECT DISTINCT Sno
	FROM SC
	WHERE Cno='CS301'AND Sno IN
		(SELECT Sno
		FROM SC
		WHERE Cno='CS306');

--查询选修了CS301号课程,但未选修CS306号课程的学上的学号
	SELECT Sno
	FROM SC
	WHERE Cno='CS301'
	EXCEPT
	SELECT Sno
	FROM SC
	WHERE Cno='CS306';
--等价于
	SELECT DISTINCT Sno
	FROM SC
	WHERE Cno='CS301' AND Sno NOT IN
		(SELECT Sno
		FROM SC
		WHERE Cno='CS306');

12.数据更新

插入

  • 向基本表插入单个元组:INSERT INTO T[(A1,...,Ak )] VALUES(c1,...,ck)
  • 将查询的结果(多个元组)插入基本表:INSERT INTO T[(A1,...,Ak)] <查询表达式>
//例子
--将学号为201816010、姓名为司马煜、性别为男、生日为1999-01-28.入校年份为2018年、专业为计算数学、所在院系为MATH的学生元组插入到Students表中
	INSERT INTO Students
	VALUES ('201816010','司马煜','男','1999-01-28','2018','计算数学','MATH')
//或
	INSERT INTO Students (Sno, Sname, Sex, Birthday, Enrollyear, Speciality, Dno)
	VALUES ('201816010','司马煜','男','1999-01-28,2018','计算数学','MATH');

--向表SC中插入一个选课记录,登记一个学号为201816010的学生选修了课程号为MA302的课程。
	INSERT INTO sc(Sno,Cno)
	VALUES('2018160107','MA302');

--设存放就餐卡登记信息关系Cardinf具有如下模式: Cardinf(Card-no,Name, Balance),其中Card-no为持卡人编号,Name为持卡者姓名,而Balance为卡中余额。假设信息工程学院要为本院每位教师办理一个校内就餐卡,直接用教师号作为持卡人编号并预存100元。可以用如下INSERT语句插入新的就餐卡信息∶
	INSERT INTO Cardinf (Card-no, Name, Balance)
	SELECT Tno, Tname,100.00
	FROM Teachers
	WHERE Dno='IE';

删除

  • DELETE语句格式为:DELETE FROM T [WHERE <删除条件>]
  • 其中T通常是基本表,但也可以是某些视图;<删除条件>与SELECT语句中的查询条件类似
//例子
--删除学号为201824010的学生记录可以用。
	DELETE FROM Students
	WHERE Sno ='201824010';
--删除所有学生的记录可以用:
	DELETE FROM Students;

--删除软件工程专业的所有学生的选课记录
	DELETE FROM SC
	WHERE Sno IN(SELECT SnoFROM Students
	WHERE Speciality ='软件工程');

修改
UPDATE语句格式

	UPDATE T
	SET A1=e1,...,Ak=ek
	[WHERE<修改条件>]

其中T通常是基本表,但也可以是某些视图;A1,…,Ak是T的属性,而e1,…,ek是表达式;<删除条件>与SELECT语句中的查询条件类似

//例子
--将职工号为B050041的教l师的职称修改为副教授
	UPDATE Teachers
	SET Title ='副教授'
	WHERE Tno ='B050041';

--将软件工程课程成绩低于60分的所有学生的软件工程成绩提高5分
	UPDATE SC
	SET Grade = Grade + 5
	WHERE Grade<60 AND Sno IN
		(SELECT Sno
		FROM Students
		WHERE Speciality ='软件工程');
	

13.视图

视图的创建和删除

  • CREATE VIEW<视图名>[ (<列名>,…,<列名>)]AS<查询表达式>[WITH CHECK OPTION]
  • <视图名>对定义的视图命名
  • <列名>为<查询表达式>结果的诸列命名<查询表达式>通常是一个SELECT查询
  • WITH CHECK OPTION表示该视图是可更新的,并且对视图进行更新时要满足<查询表达式>的查询条件
    注意:CREATE VIEW是说明语句,它创建一个视图,并将视图的定义存放在数据字典中,而定义中的<查询表达式>并不立即执行
    注意:组成视图的属性列名要么全部省略要么全部指定。如果省略了视图的各个属性名,则由SELECT子句目标列中的各个字段组成。但在下列情况下必须明确指定组成视图的所有属性列名︰
    1 )SELECT目标列中包含聚集函数或者列表达式
    2 ) SELECT子句目标列是’*’
    3 )多表连接时出现了同名属性列
    4 )需要为视图中某个列定义更合适的名字
//行列子集视图
--建立软件工程专业学生的视图SE_Students,它包含Students中除Speciality之外的所有属性和软件工程专业所有学生的信息。
	CREATE VIEW SE_Students
	AS SELECT Sno, Sname, Sex, Birthday, Dno
	FROM Students
	WHERE Speciality ='软件工程'
	WITH CHECK OPTION;

//基于多个表的视图
--建立学生成绩视图Student_Grades,它包含如下属性∶学号、学生姓名、课程名和成绩
	CREATE VIEW Student_Grades (Sno, Sname, Cname, Grade)
	AS SELECT S.Sno, Sname, Cname, Grade
	FROM Students S, sc, Courses C
	WHERE S.Sno = SC.Sno AND C.Cno = SC.Cno;

//基于视图的视图
--建立计算机科学与技术专业学生成绩视图CS_Student_Grades它包含如下属性:学号、学生姓名、课程名和成绩
	CREATE VIEW Cs_Student_Grades (Sno,Sname, Cname,Grade)
	AS SELECT S.Sno,Sname, Cname, Grade
	FROM Students S, Student_Grades sG
	WHERE S.Sno = SG.Sno AND Speciality ='计算机科学与技术';

//基于聚集函数的视图
--定义学生平均成绩视图Student_Avg_Grades,它包括如下属性∶学生的学号、姓名和平均成绩(Avg_Grade)
	CREATE VIEW Student_Avg_Grades (Sno, Sname, Avg_Grade)
	AS SELECT S.Sno, Sname, AVG (Grade)
	FROM Students S, SC
	WHERE S.Sno=SC.Sno
	GROUP BY S.Sno, Sname;

视图删除

  • 视图的删除语句的格式为∶DROP VIEW<视图名>[ CASCADE |RESTRICT ]
  • 删除视图就是把视图的定义从数据字典中删除
  • CASCADE或RESTRICT是可选的,缺省时为RESTRICT

例子

  • DROP VIEW Student_Grades或
  • DROP VIEW Student_Grades RESTRICT
  • 不能删除视图Student_Grades,因为视图CS_Student_Grades的定义依赖于它
  • DROP VIEW Student_Grades CASCADE
  • 将删除视图Student_Grades,并且级联地删除视图CS_Student_Grades
--查询软件工程专业的男生
	SELECT*
	FROM SE_Students
	WHERE Sex=‘男”;
//等价于
	SELECT*
	FROM (SELECT Sno,Sname, Sex, Birthday, Dno
	FROM Students
	WHERE Speciality ='软件工程')
	AS SE_Students (Sno, Sname, Sex, Birthday, Dno)
	WHERE Sex='男';

基于视图的更新
由于视图都是直接或间接基于基本表定义的,因此基于视图的更新最终要转换成基本表的更新
有些视图可以将更新唯一地转换成对定义它的基本表的更新,这种视图被称为可更新的视图;有些视图不能将更新唯一地转换成对定义它的基本表的更新,这种视图被称为不可更新视图
一般情况下行列子集视图都允许被更新

--向软件工程专业学生的视图SE_Students中插入一个新的记录,学号为201805109,姓名为吴畅,出生年月1999-05-04,女性,所在院系EI
	INSERT INTO SE_Students (Sno, Sname, Birthday, Sex, Dno)
	VALUES ( '201805109''昊畅','1999-05-04','女''EI');
//等价于
	INSERT INTO Students (Sno, Sname, Birthday, Sex, Dno,Speciality)
	VALUES ('201805109''昊畅''1999-05-04','女','EI','软件工程');

--删除软件工程专业学号为201805201的学生
	DELETE FROM SE_Students
	WHERE Sno='2018052017';
//等价于:
	DELETE FROM Students
	WHERE Sno='201805201' AND Speciality ='软件工程';

--将软件工程专业学号为201805268的学生姓名改为“李岩”
	UPDATE SE_Students
	SET Sname='李岩'
	WHERE Sno='201805268';
//等价于∶
	UPDATE StudentsSET Sname='李岩'
	WHERE Sno='201805268'AND Speciality ='软件工程';

//不可更新视图
--视图S_G为不可更新视图
	CREATE VIEW S_G(Sno,Gavg)
	AS
		SELECT Sno.AVG(Grade)
		FROM SC
		GROUP BY Sno;

视图的作用

  • 使用视图可以使一些查询表达更加简洁
  • 视图提供了一定程度的逻辑独立性
  • 视图可以起到安全保护作用
  • 视图使得用户能够以不容角度看待相同的数据

14.嵌入式SQL

为什么使用嵌入式SQL
SQL是一种非过程语言,用SQL语言表达查询比用通用的程序设计语言编码简单得多
我们在开发数据库应用系统时,很多时候需要使用通用程序设计语言访问数据库:

  • 一些非数据库操作,如打印报表、将查询结果送到图形用户界面中,都不能用SQL语句实现
  • SQL能够表达常见的查询,但是不能表达所有查询

例子:

  • SQL标准允许将SQL语句嵌入到C、PL/1、COBOL、Fortran、Pascal、Java等高级程序设计语言
  • 将C语言和SQL语言混合使用进行编程,使得我们可以编写任何应用程序并且使用SQL语句可以简化我们的编程,有效地实现数据库访问
  • 然而,我们需要解决如下三个问题︰
    (1)如何区分和处理两种语言的语句
    (2)两种语言的语句如何交换信息(通信)-(3如何连接数据库

14
SQL语句的识别与处理
当主语言源程序中嵌入SQL语句时,这种源程序已经不是纯的主语言源程序,通常的主语言编译系统不能处理这种源程序
解决这一问题的方法有两种:

  1. 扩充主语言编译系统,使之能处理SQL语句
  2. 预处理∶在编译前先扫描源程序,将SQL语句翻译成目标代码

通常,商品化DBMS采用预处理方法,预处理程序由DBMS开发商提供
例如,微软的sQL Server 2000提供的预处理程序nsqlprep.exe
为了能够区分源程序中的SQL语句和主语言语句,SQL规定∶

  • 所有嵌入式SQL语句都必须加前缀EXEC SQL
  • SQL语句的结束标志则因主语言的不同而异
  • 当主语言是c语言时,嵌入式SQL语句的一般形式为∶
    EXEC SQL<SQL语句>;

与主语言通信
SQL语句和主语言语句之间的信息交换(通信)可以通过

  • SQLCODE
  • 主语言变量
  • 游标

SQLCODE
每个SQL语句执行之后需要反馈一些状态信息,系统将这些状态信息存入SQLCODE中
主语言语句可以访问SQLCODE,根据结果采取相应的佛南工作
SQLCODE是一个整型变量
- 如果SQL语句成功执行,则SQLCODE=0
- 如果执行结果无数据则SQLCODE=100
- 其他情况为异常,SQLCODE取负值,其具体值依赖于实现
主语言
SQL语句与主语言语句交换信息的另一种途径是使用主语言变量使用如下形式说明的主语言变量在主语言语句和SQL语句都能使用
EXEC SQL BEGIN DECLARE SECTION;
主语言变量说明;
EXEC SQL END DECLARE SECTION;
SQL语句与主语言语句可以通过主语言变量交换信息
SQL规定:SQL语句中出现的主语言变量之前必须加冒号(∶)
指示变量
SQL支持空值(NULL ),但是主语言并无对应概念
为了解决这一问题,嵌入式SQL引进了指示变量( indicator variable )
指示变量是主语言的整型变量
每个可能被SQL语句赋予空值的主变量都可以后随一个指示变量,用来指示对应的主变量是否为空值
在SQL语句执行结束时,如果指示变量的值小于0,则其对应的主变量并未被赋值(可以视为空值)
游标
为什么需要游标

  • 主语言是面向记录的,而SQL是面向集合
  • 存在矛盾:一个SQL语句得到的结果可能是多个记录,而主语言没有办法一次处理多个记录
  • 解决该问题的方法是使用游标

游标其实就是一个数据缓冲区,暂时存放SQL语句的执行结果
使用游标需要预先说明游标,在使用前打开游标,通过专门的SQL语句逐一提取记录,并在使用完之后关闭游标

连接数据库
访问数据库前必须先建立数据库连接。SQL提供了建立和关闭数据库连接语句
建立数据库连接的语句形式为:EXEC SQL CONNECT TO<SQL服务器>[AS<连接名>][USER<用户名习]
建立到当前服务器的默认连接EXEC SQL CONNECT TO DEFAULT;
关闭数据库连接的语句形式为︰EXECSQL DISCONNECTION连接名>;

15.不使用游标的SQL语句

  • 说明性语句
  • 数据定义语句
  • 数据控制语句
  • 查询结果为单个记录的SELECT语句
  • 非交互形式的更新语句

查询结果为单个记录的SELECT语句
使用带INTO子句的SELECT语句将查询结果存放到主变量中

	EXEC SQL SELECT<选择序列>
		INTO<选择目标序列>
		<其他子句>

<选择目标序列>和<选择序列>包含相同个数的元素
<选择目标序列>中的每个元素形如︰:<主变量>[:<指示变量>]

SQL执行有两种情况

  • 查询不成功:SQLCODE≠0,除了I/O错误之外,有两种情况导致查询不成功。1.查询结果实际上并不是单条记录,而是多条记录。2.满足查询条件的元组不存在
  • 查询成功:SQLCODE=0
//例子:查询给点给学生的给定课程的成绩
--假设学生的学号已经赋予主变量Hsno。课程号已经付宇主变量Hcno,则下面的语句将检索相应的成绩,并将结果赋予主变量Hgrade
	EXEC SQL SELECT Grade
		INTO :Hgrade:igrade
		FROM SC
		WHERE Sno=:Hsno AND Cno = :Hcno; 

//非交互式更新
--某学生退学,需要删除他在Students中等级和他的所有选课记录。假设该学生的学号已经赋予主变量Hsno,以下两个语句可以完成删除工作
	EXEC SQL DELET FROM SC
		WHERE Sno=:Hsno;
	EXEC SQL DELET FROM Students
		WHERE Sno=:Hsno;

--假设信工院全体学生的专业改为软件工程,软件工程已经赋予主变量Hspeciality。使用下面程序可以修改学生的专业:
	EXEC SQL UPDATE Students
	SET Speciality =:Hspeciality
	WHERE Dno='IE';

16.使用游标的SQL语句

  • 查询结果为多个元组的SELECT语句
  • 交互式更新语句

游标说明
所有使用游标的SQL语句都必须先通过

  • 说明定义游标
  • 在使用前打开游标
  • 然后,反复推进游标指针并取当前记录进行处理
  • 最后,当所有记录都处理完之后,关闭游标

说明游标使用DECLARE语句,其格式如下:

	EXEC SQL DECLARE<游标名>
	CURSORFOR<SELECT语句>
	[<可更新性子句>]

游标的内容由打开游标时执行定义游标的<SELECT语句>决定
有一个与游标相关联的指针,初始时它指向游标第一行之前的位置
可选的<可更新性子句>是如下两种形式之一∶

	FOR READ ONLY
	FOR UPDATE [ OF<列名>....列名>]

注意:对于可更新游标,可以使用CURRENT形式的UPDATE和DELETE语句进行更新
对游标的更新要转换成对定义游标的基本表的更新。因此,SELECT语句定义的表必须是可更新的

游标的状态
游标有两种状态∶关闭状态、打开状态
初始,游标处于关闭状态,使用打开游标操作可以使它进入打开状态
打开游标的语句格式如下︰EXEC SQL OPEN游标名>;
打开游标实际上是执行相应的SELECT语句,并用查询结果形成导出表T

推进游标指针

  • 使用FETCH语句可以推进游标指针,并取出指针指向的记录
  • FETCH语句的格式如下∶
	EXEC SQLFETCH[[<推进方向>]FROM]<游标名>
	INTO:<主变量[:<指示变量>],.... :<主变量[:<指示变量];
  • 其中,<推进方向>可以是︰
	NEXT(向前推进一个记录)
	PRIOR(向后推进一个记录)
	FIRST(推进到第一个记录)
	LAST (推进到最后一个记录)
	缺省值为NEXT
  • INTO子句中的主变量必须与说明游标中的SELECT语句中的目标列表达式具有一-对应关系

关闭游标
关闭游标的语句格式如下∶EXEC SQL CLOSE一游标名>;
该语句关闭<游标名>命名的游标,释放游标占用的所有资源,使游标处于关闭状态
被关闭的游标可以再次被打开,但与定义游标的查询语句的重新执行结果相关联

查询结果为多条记录的SQL语句

  • 当查询结果为多个记录需要提交主语言程序处理时,可以查询语句说明一个只读游标
  • 打开游标就导致查询语句的执行
  • 主语言程序可以使用FETCH语句推进游标指针,取出每个查询结果进行处理
  • 最后,关闭游标
//例子
--查询某个院全体学生的信息(学号、姓名、性别和年龄),要查询的院系号由用户在程序运行过程中指定,放在主变量Hdno中
--对应的主变量定义如下︰
	EXEC SQL BEGIN DECLARE SECTION;
		char Hsno[9];
		char Hsname[8];
		char Hsex[2];
		int Hage;
		char Hdno[ 10];
	EXEC SQL END DECLARE SECTION;
--实现该查询的程序段
	EXEC SQL DECLARE YXCURSOR FOR
		SELECT Sno,Sname,Ssex,Sage
		FROM Students
		WHERE Dno=:Hdno;
	EXEC SQL OPEN YX
	Hdno='IE';
	WHILE(1)/*用循环结构逐条处理结果集中的记录*/
	{
		EXEC SQL FETCH YX
			INTO : HSno,: HSname,: HSsex,: HSage;
			if(sqlca.sqlcode<>0) break;
			...... /*由主语言语句进行进一步处理*/
	}
	EXEC SQL CLOSE YX;

Current形式的更新语句
当更新条件很明确时,容易在主语言程序中使用SQL的更新语句。然而,有时更新条件并不容易明确表述,需要人工干预

  • 例如,假设学生李明退学,我们需要删除他在Students和SC中的记录,但是我们不知道李明的学号。我们不能直接删除Sname=‘李明’的Students记录,因为李明是一个常见的名字,叫李明的学生可能不止一位
  • 我们可以通过观察学生得更多信息(不易表述),决定哪位李明是退学的李明
  • 借助于可更新游标,我们可以完成这一任务

当一个可更新游标被打开之后,可以取出它的每个元组进行观察,确定是否需要更新,并在需要更新时,使用带CURRENT形式的DELETE或UPDATE语句对游标指针指示的行进行删除或修改
带CURRENT形式的DELETE语句格式如下:

	DELETE FROM T
	WHERE CURRENT OF<游标>

其中T是基本表,游标定义在基本表T上,并且是可更新的。该语句从表T中删除游标指针指向的对应行

带CURRENT形式的UPDATE语句格式如下∶

	UPDATE T
	SET A1=e1,..., Ak=ex
	WHERE CURRENT OF<游标名>

其中T是基本表,游标定义在基本表T上,并且是可更新的
A1…Ak是T的属性,而e1… ek是表达式

//从students和sC中删除某学生的记录的程序段如下:
	char YN;	//变量YN不在sQL语句中使用
	EXEC SQL BEGIN DECLARE SECTION;
		char Givenname[8];
		char Hsno[9];
		char Hsname[8];
		char Hsex[2];
		char Henrollyear[4];
		char Hspeciality[20];
		char Hdno[4];
	EXEC SQL END DECLARE SECTION;
	EXEC SQL DECLARE Student_Cursor CURSOR FOR
	SELECT Sno,Sname,Sex,Enrollyear,Speciality,Dno
	FROM Students
	WHERE Sname=:Givenname
	FOR UPDATE;
	Givenname='李明';
	EXEC SQL OPEN Student_Cursor;
	EXEC SQL FETCH Student Cursor
	INTO :Hsno,:Hsname,:Hsex,:Henrollyeat,:Hspeciality,:Hdno;
	while (SQLCODE=O){
	printf ('%s %s %s %s %s %s \n',Hsno, Hsname, Hsex, Henrollyear,Hspeciality, Hdno);/*是否删除*/
	printf("delete?(Y-delete,N-not delete): ");
	scanf("%cin",&YN);
	if (YN=='y' || YN=='Y'){
	/*删除sC元组,不用游标*/
	EXEC SQL DELETE FROM SC
	WHERE Sno=:Hsno;
	/*删除students元组,使用游标*/
	EXEC SQL DELETE FROM Students
	WHERE CURRENT OF Student_Cursor;
	/*完成,退出*/
	break;
	}
	/*取出下一位学生的信息*/
	EXEC SQL FETCH Student_Cursor
	INTO :Hsno,:Hsname,:Hsex,:Henrollyear,Hspeciality,:Hdno;
	}
	/*关闭游标*/
	EXEC SQL CLOSE Student Cursor;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值