从 1970 年美国 IBM 研究中心的 E.F.Codd 发表论文到 1974 年 Boyce 和 Chamberlin 把 SQUARE 语言改为 SEQUEL 语言,到现在的 SQL2、SQL3,SQL 一直都在不断完善和发展之中。SQL(结构化查询语言)虽然名为查询,但实际上具有定义、查询、更新和控制等多种功能。
SQL 数据库的体系结构也是三级结构,但术语与传统关系模型术语不同,在 SQL 中,关系模式称为“基本表”,存储模式称为“存储文件”,子模式称为“视图”,元组称为“行”,属性称为“列”。
SQL 模式的创建可简单理解为建立一个数据库,定义了一个存储空间,其句法是:CREATE SCHEMA <模式名> AUTHORIZATION <用户名>。
撤销 SQL 模式的句法为:DROP SCHEMA <模式名> [ CASCADE | RESTRICT ],CASCADE 表示级联式方式,执行时将模式下所有基本表、视图、索引等元素全部撤销;RESTRICT 表示约束式,执行时必须在 SQL 模式中没有任何下属元素时方可撤销模式。
“模式”这个名词学术味太重,大多数 DBMS 中不愿采用这个名词,而是采用“数据库”,而语句采用 CREATE DATABASE 和 DROP DATABASE。
各类型的值可以进行比较操作,但算术操作只限于数值型。两个日期类型的差是时间间隔类型(INTERVAL),日期加或减去 INTERVAL 可得到另一个日期。
下面是基本表的创建,其中,完整性约束包括主键子句(PRIMARY KEY)、检查子句(CHECK)、外键子句(FOREIGN KEY):
CREATE TABLE T
(
T# CHAR(4) NOT NULL,
TNAME CHAR(8) NOT NULL,
TITLE CHAR(10),
PRIMARY KEY (T#)
)
CREATE TABLE C
(
C# CHAR(4) NOT NULL,
CNAME CHAR(8) NOT NULL,
T# CHAR(10),
PRIMARY KEY (C#),
FOREIGN KEY (T#) REFERENCES T(T#)
)
基本表的修改句法如下,需注意,新增的列不能定义为 NOT NULL。
ALTER TABLE <基本表名> ADD <列名> <类型>
ALTER TABLE <基本表名> DROP <列名> [ CASCADE | RESTRICT ]
ALTER TABLE <基本表名> MODIFY <列名> <类型>
DROP TABLE <基本表名> [ CASCADE | RESTRICT ]
索引的创建和撤销:
CREATE [UNIQUE] INDEX <索引名> ON <基本表名> (<列名序列> [ASC][DESC])
DROP INDEX <索引名>
SELECT <列名表(逗号隔开)> FROM <基本表或视图> [ WHERE <行条件表达式> ] [ GROUP BY <列名序列> ] [ HAVING <组条件表达式> ] [ ORDER BY < 列名 [ ASC | DESC ]> ]
(1)检索学号为 S3 的学生所学课程的课程名与任课教师名。
SELECT CNAME,TEACHER FROM C WHERE C# IN (SELECT C# FROM SC WHERE S# = 'S3')
SELECT CNAME,TEACHER FROM SC,C WHERE SC.C# = C.C# AND S# = 'S3'
(2)检索至少选修 LIU 老师所授课程中一门课程的女学生姓名。
SELECT SNAME FROM S WHERE SEX = '女' AND S# IN (SELECT S# FROM SC WHERE C# IN (SELECT C# FROM C WHERE TEACHER = 'LIU'))
SELECT SNAME FROM S,SC,C WHERE SEX = '女' AND S.S# = SC.S# AND SC.C# = C.C# AND TEACHER = 'LIU'
(3)检索 WANG 同学不学的课程的课程号。
SELECT C# FROM C WHERE C# NOT IN (SELECT C# FROM SC WHERE S# = (SELECT S# FROM S WHERE SNAME='WANG'))
(4)检索至少选修两门课程的学生学号。
SELECT S# FROM SC GROUP BY S# WHERE COUNT(S#) >= 2
SELECT DISTINCT S# FROM SC AS X, SC AS Y WHERE X.S# = Y.S# AND X.C# != Y.C#
(5)检索全部学生都选修的课程的课程号与课程名。
SELECT C#,CNAME FROM C WHERE C.C# IN (SELECT C# FROM SC GROUP BY C# WHERE COUNT(C#) = SELECT COUNT(S#) STUCOUNT FROM S GROUP BY S#)
SELECT C#,CNAME FROM C WHERE NOT EXISTS (SELECT * FROM S WHERE NOT EXISTS (SELECT * FROM SC WHERE S# = S.S# AND C# = C.C#))
INSERT INTO <基本表名> [(<列名序列>)] VALUES (<元组值>) // 注:元组值可以连续插入
INSERT INTO <基本表名> [(<列名序列>)] <SELECT 查询语句>
DELETE FROM <基本表名> [ WHERE <条件表达式>]
UPDATE <基本表名> SET <列名>=<值表达式>, [ <列名>=<值表达式> ] [ WHERE <条件表达式> ]
CREATE VIEW <视图名> (<列名序列>) AS < SELECT 查询语句 >
DROP VIEW <视图名>
对视图(虚表)的查询与基本表一致,但更新操作受到下面三条规则的限制:
在主语言(如C,Java,C# 等)中使用的 SQL 结构称为 嵌入式 SQL。在终端交互方式下使用,称为 交互式 SQL。
存储设备商的数据库是用 SQL 语句存取的,数据库和主语言程序间信息的传递是通过共享变量实现的,这些共享变量要用 SQL 的 DECLARE 语句说明。SQL2 规定,SQL_STATE 是一个特殊的共享变量,起着解释 SQL 语句执行状况的作用。它由 5 个字符组成,当一个 SQL 语句执行成功时,系统自动给它赋上全零值(“00000”)表示未发生错误;否则其值为非全零,表示发生的各种错误情况。程序可以根据 SQL_STATE 的值转向不同的分支,以控制程序的流向。
例如,在 C 语言中可用下列形式声明共享变量。下面四行组成一个说明节,3 个共享变量,SQL_STATE 长度是 6 而不是 5 是因为 C 语言规定字符串变量值应有结束符 “\0”。
EXEC SQL BEGIN DECLARE SECTION;
CHAR sno[5], name[9];
CHAR SQL_STATE[6];
EXEC SQL END DECLARE SECTION;
SQL DDL 语句,只要加上前缀 EXEC_SQL 和结束标志 END_EXEC 就能嵌入在主语言中使用。但 SQL DML 语句在嵌入使用时,要注意是否使用了游标机制。
由于增删改语句不返回数据结果,因此只需要加上前后缀就可以在主语言中使用。而对于 SELECT 语句,如果已知查询结果是单元组时,在加上前后缀后,还应再增加一个 INTO 子句,指出找到的值应送到共享变量中去。
例1. 在基本表 S 中,根据共享变量 givensno 的值检索学生的姓名、年龄、性别。“:”作为共享变量的前缀,与数据库中变量的作区别。程序已预先给出 givensno 的值,而 SELECT 查询结果将送到变量 sn、sa、ss 中。
EXEC SQL SELECT sname, age, sex INTO :sn, :sa, :ss FROM s WHERE s# = :givensno;
例2. 在基本表 S 中插入一个新学生,诸属性值已在共享变量中。
EXEC SQL INSERT INTO s (s#, sname, age) VALUES (:givesno, :sn, :sa);
例3. 根据学生的姓名共享变量 sn,删除一个学生的成绩。
EXEC SQL DELETE FROM SC WHERE s# = (SELECT s# FROM S WHERE SNAME = :sn);
例4. 把课程名为 MATHS 的成绩增加某个值,该值在 raise 中已给出。
EXEC SQL UPDATE SC SET SCORE = SCORE + :raise WHERE C# = (SELECT C# FROM C WHERE CNAME = 'MATHS');
当查询结果是多个元组时,此时主语言程序无法使用,一定要使用游标机制把多个元组一个一个地传送给主语言程序使用。
具体过程:
例1. 在基本表 SC 中检索某学生(学号由共享变量 givensno 给出)的学习成绩信息(S#, C#, SCORE)。
#define NO_MORE_TUPLES ! (strcmp (SQLSTATE, "02000"))
void sel()
{
EXEC SQL BEGIN DECLARE SECTION;
char sno [5], cno [5], givensno [5];
int g;
char SQL_STATE [6];
EXEC SQL END DECLARE SECTION;
scanf ("%s", givensno);
EXEC SQL DECLARE scx CURSOR FOR
SELECT S#, C#, SCORE FROM SC WHERE S# = :givensno;
EXEC SQL OPEN scx;
while(1)
{
EXEC SQL FETCH FROM scx INTO :sno, :cno, :g;
if (NO_MORE_TUPLES) break;
print("%s, %s %d", sno, cno, g);
}
EXEC SQL CLOSE scx;
}
之前的游标在推进时只能一行行推进,且不能返回,这给使用带来了一定的麻烦。SQL2 提供了卷游标(scroll cursor)技术解决这个问题,卷游标可以进退自如。
EXEC SQL DECLARE scx SCROLL CURSOR FOR <SELECT 子句>
EXEC SQL FETCH NEXT FROM <游标名> INTO <变量表>
EXEC SQL FETCH PRIOR FROM <游标名> INTO <变量表>
EXEC SQL FETCH LAST FROM <游标名> INTO <变量表>
EXEC SQL FETCH RELATIVE -3 FROM <游标名> INTO <变量表>
EXEC SQL FETCH ABSOLUTE 5 FROM <游标名> INTO <变量表>
有时,在实际问题中,源程序往往还不能包括用户的所有操作,用户对数据库的操作需要在系统运行时才能提出来,这就要使用嵌入式 SQL 的动态技术才能实现。
EXEC SQL PREPARE <动态SQL语句名> FROM <共享变量或字符串>
EXEC SQL EXECUTE <动态SQL语句名>
存储过程 是使用 SQL 语句和流程控制语句编写的模块,存储过程经编译和优化后存储在数据库服务器端的数据库中,使用时调用即可。
CREATE PROCEDURE <存储过程名> (<参数>)
<局部声明>
<函数体>; -- 局部声明 和 参数 是可选的。
-- 通常,每个参数应有一个 SQL 数据类型,还应有一个参数模式:IN(输入值)、OUT(输出值),INOUT(可输入可输出)
也可声明一个函数,但必须给出返回类型(函数的限制比较多,相对于存储过程函数比较鸡肋!):
CREATE FUNCTION <函数名> (<参数>)
RETURNS <>
<局部声明>
<函数体>;
SQL / PSM 是 SQL 标准的一部分,指定了如何编写持久存储模块,提供流程控制语句来表示存储过程的应用逻辑。
IF <条件> THEN <语句列表>
ELSEIF <条件> THEN <语句列表>
......
ELSEIF <条件> THEN <语句列表>
ELSE <语句列表>
END IF;
-- 标准的 WHILE 循环结构
WHILE <条件> DO
<语句列表>
END WHILE
-- 标准的 REPEAT 循环结构
REPEAT
<语句列表>
UNTIL <条件>
END REPEAT;
-- 基于游标的循环结构
FOR <循环名> AS <游标名> CURSOR FOR <查询> DO
<语句列表>
END FOR