数据库系统基础教程四:数据库语言SQL

来源:互联网 发布:淘宝开店页面 编辑:程序博客网 时间:2024/06/05 08:22

        前面各节介绍了关系代数、关系设计理论、高层概念表示,本节之后将介绍SQL语言。

        目前主流的数据库如MySQL、Oracle等只是实现了SQL标准的部分,而且各有扩展,具体的细节需要查询各个数据库的文档。本节只是作为SQL的入门来进行介绍。

        MySQL文档库:http://dev.mysql.com/doc/

        Oracle的文档库:http://www.oracle.com/technetwork/cn/indexes/documentation/index.html

        6 SQL查询语言

        6.1 简单查询

        典型的查询为SELECT-FROM-WHERE形式,FROM子句给出查询所引用的关系,WHERE子句用来给出选择条件,SELECT决定哪些属性应该被显示出来。

        先给出一个示例:SELECT title AS name, length AS duration FROM Movie WHERE studioName='Disney' or studioName like '%ney%'  order by duration;

        从关系代数的角度,SELECT用来投影关系的字段,这个字段可以使用AS来进行重命名。基于扩展也可以将常量表达式或属性的表达式进行投影。

        使用ORDER BY <attr,...>来进行结果的排序,默认为ASC,即升序,也可以在后面加DESC来降序排列。ORDER BY位于任何语句之后,执行时在FROM和WHERE的结果上进行排序,排序之后再应用SELECT。ORDER BY后的列表,也可以包含表达式。

        WHERE子句来实现选择操作,条件可以通过逻辑运算符AND,OR,NOT来组合,分别为并、或和非运算。

        此外SQL提供了模式匹配用于字符串比较。使用方式为 like 'pattern',模式为一个可能使用%或_的字符串,%可以匹配源中任意长度的字符串_可以匹配任意一个字符。

        SQL允许属性有一个特殊值NULL。在WHERE子句中,当对NULL进行运算时有两个重要的规则:第一,NULL和任何值进行算术运算,其结果仍为NULL。第二,当使用比较运算符时,NULL和任意值比较,结果均为UNKONWN。如果希望断定某属性是否为NULL,则需要使用关键字IS NULL或IS NOT NULL。

        上面介绍到NULL的比较会产生UNKONWN的BOOL值,与通常的TRUE/FALSE并列。我们可以将TURE视为1,FALSE视为0,而UNKONWN视为0.5,则有如一规则:

        第一:两个布尔值之间AND运算取两者最小的值。例如存在FALSE的比较结果为FALSE,而TRUE和UNKONWN的比较为UNKONWN。

        第二:两个布尔值之间OR运算取两者最大的值。

        第三:布尔值x的非为1-x。即TRUN和FALSE可以互非,而UNKNOWN的非是其自身。

        6.2 多关系查询

        SQL用简单的方式在一个查询中处理多个关系,即在FROM子句中列出每个关系,然后在SELECT和WHERE子句中引用任何出现在FROM子句中关系的属性。

        示例:SELECT name FROM Movie as m ,MoveExec as e WHERE m.title='star war' AND m.produceNo = e.certNo

        当查询涉及多个关系的时候,关系中可能会有属性重名。此时需要以明确的方式来指明我们选择的是哪个关系上的属性。SQL通过在属性前面加上关系名和一个点.来解决这个问题。

        关系代数中可以用集合操作来组合关系,SQL中对应的操作作用在查询结果上。关键字UNION, INTERSECT,EXCEPT分别对应U,M,-。当这此关键字用于两个查询时,查询应该分别用括号括起来。

        6.3 子查询

        当某个查询是另一个查询的一部分时,称其为子查询。子查询还可以拥有下一级子查询,如此递推。前面介绍了通过使用集合操作合并两个子查询的结果。下面介绍三种新的子查询的形式。

        6.3.1 子查询返回单个值用于WHERE子句

        可以将此类查询用于上层查询的WHERE子句中。

        例如SELECT name FROM MoveExec WHERE certNo = (SELECT produceNo FROM Move WHERE title = 'star war');

        6.3.2 返回关系的子查询用于WHERE子句

        SQL定义了如下几个运算符,作用于关系,可以返回BOOL结果。

        s IN R  如果s存在于R中

        EXIST R  关系R非空

        s > ALL R 表示 s >max(R)    

        s > ANY R 表示s > min(R)

        基于上面的定义,如果一个元组和一个关系有相同的属性个数,则可以使用上面的运算符来组合子查询。例如:

        SELECT producerNo FROM Movies WHERE (title,year) IN ( SELECT mtitle ,myear FROM StarsIn WHERE startName = 'Harrison Ford');

        上面介绍的子查询只需要计算一次,返回结果给上层查询。更复杂的查询要求子查询计算多次,每次需要从上层传递外部元组变量给子查询,称此类查询为关联子查询。

         SELECT title FORM Movies AS old WHERE year < ANY ( SELECT year FROM Movies WHERE title = old.title);

         6.3.3 返回关系的子查询用于FROM子句

         返回的关系需要起个名字以便用于FROM子句中,例如:SELECT title FROM Movies, (SELECT * from Movies) AS old where old.title =title and old.year < year;

        6.3.4 SQL的连接表达

        本节介绍的连接运算符是冗余的,它可以用SELECT FROM WHERE表达式替换。最简单的连接为笛卡儿积,但实际上较少使用,形式为R CROSS JOIN S。CORSS可以省略。

        θ连接形式:R JOIN S ON  <条件>

        示例:SELECT * FROM Movies JOIN StarsIn ON title = mTitle and year = mYear;

        6.3.5 θ外连接

        形式:R [ LEFT|RIGHT ] OUTER JOIN  S ON <条件>

        6.3.6 自然连接

        自然连接形式:R NATURAL JOIN S  

        由于自然连接自行为在同名且值相等的属性上连接,因此不需要外加条件。因此上面的示例在两个表的title和year字段同名时,可以化简:

        SELECT * FROM Movie NATURAL JOIN StarsIn .

        MySQL使用INNER JOIN 来表示自然连接,但可以通过 using 来指定使用的列。此外MySQL在内连接时会自行判断载入表的先后顺序,用户也可指定载入顺序,这需要使用MySQL的扩展连接关键字STRAIGHT_JOIN。其语法为 R STRAIGHT_JOIN S on 

        6.3.7 外自然连接

        外自然连接形式:R NATURAL [ LEFT | RIGHT ] OUTER JOIN S

        示例:SELECT * FROM Movies NATURAL LEFT JOIN StarsIn; 

        6.4 全关系操作

        6.4.1 消除重复

        如果希望结果中不出现重复元组,可以使用SELECT DISTINCT。然而消除重复的代价较高,只有在排序或分组才能保证相同的元组紧挨在一起。

        与SELECT不同的是,UNION等集合操作,默认是不消除重复的。如果希望在集合运算中保留重复,需要在集合运算符后加ALL关键字。

        我们也可以从 聚集操作符中消息重复,方法是在聚集运算符内对列加DISTINCT,这样可以在聚集操作符之前消除重复。

        6.4.2 分组

        分组使用group by可以对列进行分组和聚集。当元组中含有空值时要注意如下规则:

        空值在任何聚集操作中都被忽略;除了计数以外,对空包执行聚集操作,结果均为NULL。空包的计数结果为0。

        6.4.3 HAVING子句

        如果需要对基于某些分组聚集的性质选择分组,可以在GOURP BY后面加上HAVING子句,HAVING子句后跟一个分组的条件。表示分组必须满足所给的条件,才可以作为结果返回。

       示例,列出在2000之前制作过电影的制片人所摄制的电影个数

        SELECT name,COUNT(title) FROM movie,movieexec WHERE movie.`producerC#` = movieexec.`cert#`GROUP BY name HAVING MIN(year)<2000;

        6.5 数据库更新

        6.5.1 元组插入

        INSERT INTO R(a1,a2,...an) VALUES(v1,v2,...vn),属性列表可以省略,此时必须保证属性值的顺序与关系属性的标准顺序一致。可以通过子查询向关系中插入计算出的元组集合。

        例如:INSERT INTO Movies VALUES ('star war', 1980, 120, 'POP')。

        6.5.2 删除

        DELETE FROM R WHERE <条件>,如果没有条件,则所的元组都会被删除。

        6.5.3 更新

        UPDATE R SET <col = val,...> WHERE <条件>。

        6.6 事务

        先介绍串行化的概念,为了支持事务的并行性,需要事务都是可串行化,即这些事务执行起来必须表现的串行执行的,没有重叠并行。

        除了串行化,另一概念是原子性,即事务要么执行成功,要么没有执行,不存在执行一部分的问题。

        6.6.1 SQL中提交事务

        SQL支持事务,使用START TRANSACTION标识事务开始,使用COMMIT提交事务或使用ROLLBACK回滚事务。

        通常多个读事务可以并发执行,但涉及到写事务则需要考虑具体情况。SQL允许告诉系统下面要执行的事务是性质为只读还是读写的。

        SET TRANSACTION READ [ ONLY | WRITE ] 

        6.6.2 脏读与隔离级别

        脏数据指还没提交的事务所写的数据的通用术语。脏读是对脏数据的读取。脏读的风险是写数据的事务可能回失败,那么读取的数据就是无效的。脏读有时是重要的,有时脏读是无关紧要的,脏读可以提高性能。

        SQL设备了四种隔离层次,通过设置事务的隔离层次,可以让我们控制如何处理脏数据。        

隔离解别脏读不可重复读幻读Read UncommittedYYYRead CommittedNYYRepeatable Read (default)NNYSerializableNNN

        隔离层次对事务进行设置。如SET TRANSACTION  ISOLATION LEVEL READ COMMITED。

       对于读提交层次,SQL允许同一个查询执行多次并获得不同的结果。只要结果反应了已经提交事务定写入的数据。

       对于可重复读层次,如果第一次检索到一个元组,则后续重复这个查询会再次检索到同一元组。然而,后续操作会可能会遇到幻象元组。例如其他事务插入新元组,而本事务也想插入同一新元组,此时就冲突了,幻象元组突然“出现”起作用了。

       在串行话层次上,事务必须等待前一个执行结束方能执行。


        7 约束与触发器

        7.1 键与外键约束

        7.1 外键的创建

        我们在第二节已经看到主键的两种定义方式,外键与之类似,也有两种定义方式:

        在单个属性上的外键:在属性声明之后添加REFERENCE <TABLE>(<ATTR>)

        如果外键为多个属性,则必须在CREATE TABLE语句的属性列表之后追加声明 FOREIGN KEY(<ATTR LIST>) REFERENCE <TALBE> (<TABLE ATTR LIST>)  

        示例:CREATE TABLE Studio (name CHAR(20) PRIMARY KEY,presC# INT, FOREIGN KEY (presC#) REFERENCE MovieExec(cert#) ) ;

        7.1.2 维护引用完整性

        外键约束有当违反时有三种不同的处理方式:

        第一,缺省的拒绝违法操作;

        第二,级联(CASCADE)原则,被引用属性的改变被应用到外键上。

        第三,置空NULL,被引用的关系上的更新影响外键时,外键被置为空值。

        这三个选项可以独立的选择删除和修改,同外键一起声明。声明方法为在ON DELETE 或ON UPDATE 后加SET NULL 或 CASCADE选项。

        示例:CREATE TABLE Studio (name CHAR(20) PRIMARY KEY,presC# INT, FOREIGN KEY (presC#) REFERENCE MovieExec(cert#) ON DELETE SET NULL ON UPDATE SET CASCADE);

        7.1.3 延迟约束的检查


        7.2  属性与元组约束

        7.2.1 非空约束

        CREATE TABLE中与属性相连的简单约束为NOT NULL,其作用为不允许元组的此属性取NULL。

        7.2.2 基于属性的CHECK约束

        更复杂的约束是将保留字CHECK和圆括号括起来的条件附加在属性声明上,此条件为此属性的每个值都必须满足的条件。原则上CHECK条件可以是任何在WHERE了句中允许的描述。CHECK约束是在元组在此属性上获得新值时被检查,如果此属性没有变化,则不会执行检查。

        示例:CREATE TABLE ....., gender CHAR(1) CHECK (gender IN ('F','M') )

        7.2.3 基于元组的CHECK约束

        如果CHECK涉及多个属性,则需要在CREATE TABLE的属性定义之后声明CHECK,这点类似于键约束。

        示例:在添加影星时,如果为男性,则不能以小姐字样开头

        CREATE TABLE MovieStar (name CHAR(30) PRIMARY KEY, gender CHAR(1), CHECK (gender IN ('F','M') OR name not like 'Ms.%' )

        7.3  约束修改

        7.3.1 约束命名

        在约束前添加CONSTAINT 和约束的名字即可。

        对于主键:name char(30) CONSTRAINT name_pk PRIMARY KEY

        对于约束: gender char(1) CONSTAINT no_mr CHECK (gender IN ('F','M'))

        7.3.2 约束的修改

        约束在命名之后,可以根据名字来修改。

        约束删除:ALTER TABLE tbl_name DROP CONSTRAINT cons_name;

        约束添加:ALTER TABLE tbl_name ADD CONSTRAINT cons_name CHECK/PRIMARY KEY ()/FOREIGN KEY()

        7.4 断言

        CREATE ASSERTION <name> CHECK (<condition>)

        DROP ASSERTION name

        MySQL并不支持断言,不在此详细介绍。

        7.5 触发器



        8 视图与索引

        通常我们所说的视图即虚拟视频,其并不进行存储,但可以对其查询,就好像其确实存在于数据库中一样。视图可以被物化,需要定期地进行构造并存储。索引是一种物化视图。

        8.1 虚拟视图

        SQL中视图的定义方式为CREATE VIEW <viewname> AS <SELECT 语句>,即视图其实是对一次查询结果的虚拟存储,方便后续使用。视图在定义之后,和真实的关系表一样,可以在FROM、WHERE子句中使用。在定义视图时还可以重命名属性,方法为在视图名后加一对圆括号,将视图的属性定在括号内。

        示例:CREATE VIEW DisneyMovie ( film, year ) AS SELECT title,year FROM Movies Where studioName = ‘disney’;        

        删除视图:DROP VIEW viewname;  删除视图不影响原始表的数据。

        SQL中规定对于一些简单的视图,可以对视图进行插入、删除和更新,我们不在此进行描述。

        8.2 SQL索引及选择

        8.2.1 索引的创建和删除

        关系中属性的索引是一种数据结构,可以提高对应属性上查询的效率,常用的实现为二叉查找树,键为属性的可能的值,键对应的值为相应分组的存储位置。索引可以极大的提高相应列上查找的速度,如果没有索引,我们不得不遍历所有的元组从中过滤出符合条件的部分。

        索引创建语法:CREATE INDEX idx_name ON R(column,...),在关系R的相应列上创建一个索引,可以针对多个属性作联合索引,此时要注意属性列表的顺序选择。

        索引删除:DROP INDEX idx_name

        除了上面介绍的索引创建方式,另一种常用的索引创建方式是在创建表时,将相应的列声明为KEY,这样DBMS会默认为相应列创建索引。

        8.2.2 索引的选择

        索引不但可以加快查找,在相应关系连接时,也可以加快操作,但索引的维护是有代价的,当相应列有新元组插入、删除和修改时,都需要更新索引。

        关系上最有用的索引为主键创建;其次,如果某些属性虽然不是键,但几乎可以作键用,则也可以创建索引;最后,如果元组在某些属性上是聚集的,则可以为此属性添加索引。

        为了计算最佳索引,需要统计查询和更新在基于关系的哪些属性上最为频繁的发生。当对频繁度有排序后,按从高到低依次添加索引,会为我们带来最大的效率。

        8.3 物化视图

        SQL标准支持物化视图,但在MySQL中并不是天然支持的。

        物化视图创建:CREATE MATERIALIZED VIEW viewname AS <SELECT .....>

        物化视图的更新不需要在基本表改变时进行,可以离线的在数据库不繁忙的时候执行。

        视图和物化视图可以简化查询语句。


        9 存储过程

        

       

        10 权限管理

        授权:GRANT <权限列表> ON <DB ELEMENT> TO <USER> [WITH GRANT OPTION]

        收权:REVORK <权限列表> ON <DB ELEMENT> FROM  <USER> [ CASCADE| RESTRICT ]

        DBMS使用授权图来跟踪所授予出到的权限。

        参考文献

        MySQL隔离级别 http://blog.csdn.net/taylor_tao/article/details/7063639

        

原创粉丝点击