idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P...

227
!"#$ !"#$%&’()*+! ,-. !"#$/0’1234! 56789:;<=>?@AB9: ’CD! E $%& ’F!GH! $%&IJ’KLMNO" PQMNO" R4NO" $%STUVW" MX Y-" Z;[\()]^_‘#ab()cdefghiBj7# 56!"klmno$%&’pqrs! tu()vw! Rx$%&rsy$%z{<|}~ ! $%ym3# !"#$! %&’(#)*+,$ !"! #$%&$’&’ ! "()!"$)##%& ! "(&!"("!’(( -./01$23456!789:;! <:;=>?@A# -.89:;BCDE89FG! CHIJKLMNO0PQ2R! MNST! RUVMNWX%YZ O0[\]! ^L_‘aCbcPQ! MNL_‘adXefghij9# M.L!kl& *+, mn $%&IJ()<* ( ( )$3! )**+(, & ~I*+-$"./’0*)’1)2+*’* ! 3 $* !" 3 * !# 3 $%&IJ4 4*+ !$ 356011310 ¡!¢"£ 7-6$%¤¥& )**+ ¦*1111)§ 6!=$3 o p$¨© 899: $(( ;;;(9<:(=>?(=@ q k$ 1***A, 7rs$ *1*’+)//*1/2 tHuv$ *1*’+)//+B+B wxky$ª ! « zxky$¬-® &{=$ |}=$ ~=$¯"°±°4\² -$ 1A2C)+* ! &$ 1,32 ! m$ 00/³¥ ! $ )**+´,µ¦1 ! )**+´,µ¦ 1 1¶·¸ . $ -$"./’0*)’1)2+*’* ( 56 + A*02 & m$ 1 %! *** $ !! ¹

Transcript of idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P...

Page 1: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

书书书

! " # $

!"#$%&’()*+!,-.!"#$/0’1234!56789:;<=>?@AB9:

’CD!E$%&’F!GH!$%&IJ’KLMNO"PQMNO"R4NO"$%STUVW"MX

Y-"Z;[\()]^_‘#ab()cdefghiBj7#

56!"klmno$%&’pqrs!tu()vw!Rx$%&rsy$%z{<|}~��

�!��$%y��m�����3������#

!"#$!%&’(#)*+,$!"!’#$%&$’&’!"()!"$)##%&!"(&!"("!’((-./01$23456!789:;!<:;=>?@A#

-.89:;BCDE89FG!CHIJKLMNO0PQ2R!MNST!RUVMNWX%YZ

O0[\]!̂ L_‘aCbcPQ!MNL_‘adXefghij9#

M.L!kl&*+,’mn

$%&IJ()�<*�(�����()��$����3��!)**+(,&����~����I�*+’

-$"./’0*)’1)2+*’*

!3$*!"3�*!#3$%&IJ4��� 4*+!$356011310

�¡�!¢"£7-6$%¤¥&)**+’¦*1111)§

6 ! =$����3�� o p$�������¨�©

899:$((;;;(9<:(=>?(=@ q k$1***A,7 r s$*1*’+)//*1/2 tHuv$*1*’+)//+B+B

wxky$ª!«

zxky$¬­®

& { =$

| } =$

~ � =$¯�"°±°��4\²

� -$1A2C)+*!&�$1,32!�m$00/³¥

! �$)**+´,µ¦1�!)**+´,µ¦1111111¶·¸

. �$-$"./’0*)’1)2+*’*(56+A*02& m$1%!***� �$!!¹

Page 2: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

-.5DE!F75-E.

%&’(

!"#$%&’()*+,-./0123*45!6789:;<=>?*@ABCDEFGH*I+J!KLDEFMHNA*O.J!PQRSTU-VWXY*.Z[*9:;<=>?\]^_‘ab5c9:;<=01*dNef"eg!h#iS !" j#$abck##$jlmbnock##%jlmbpockqrF01sstab5c!uv.wF9:;0c-kTxyRSnz{|!}L5‘~#���5c9:;01^���!RS����*0c��D�#0cV�D�#0cMH��D�->?PQ��D�/��D�!�]�PQn�>?*0c-kT��"

ab5c9:;ck*0c�kT���PQn�9:;>?*��!�r<=0�*��- ¡¢�u£��*aN¤]s¥!n¦RSckx§¨©{|*9:;<=0�ª^«ab5c*ab ¡¬e]­®¯°z"^�±²-³´«ab5cF9:;<=01J*{|!¨µ�<=0� ¡J*{|!¶·¸�«ab5c*9:;ck ¡-<=��0c@N!F01s¹º0c»¼½¾7<$* ¿-«ab5c*5YÀÁÂ!ÃÄ5cÅÆ62Ç}ÅÆÈ�É0�"È�É0�* ¡ÊF $ËÌckÍÎ#ÏÐck ¡#P1<=Î?%!¶·~0�²Ñ«ab5c*{Ò0cÓÔ#0cxÕ#0cÖ×-0cØÙ/"

È�É0�F2ÇÚ�rDE�ÛÂnv�ȤÜÝ¢-¨b"!(y§ck³±*gÞ!ßàág6789:;<=.o>?*P

Q@A"0�ØÙ~�ÈÓâ^��!ãä�ÈÓâ-ÝÓ*��à�!aåVW-à��æ"

#(ãä0c@N!çè0c³±"0�NXßà‘é’*0c@N!êëìí0cØÙ-��D�*îïx§"Fðñ0�ØÙ-òóD�·ôõDEöM01#÷øXY�VWXY*PQ!^cùúû#XY#öMüý³±÷þÿ!"

Page 3: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

!!!!!mn���������

&(V"Í#$%!&Åab!��MH"2Ç0� ¡*ab’(�<=���-<=d)�&¨µôõðñ}*+�ns¥Ý,��-./*{Ò0�012345Æ!6(�]Í#0�&O7}89òóDEab5c9:;<=0cØÙ-��D�îï]­*0�"

’(d:n;‘È!�Ó¸<"<=���-<=d)�0�N¸<!¶n=��>~S‘ÈRSB¶ØÙ¨b*0�"?Ó/0��n@�‘é’*º�&�È0��AB0�~C0c�DE*º�&FG0��H!0�*º�!VE0��ÉIJ¸<"

((’K<$!ñ{LV"FM40�2Ç·N’K«��<$FýNTUÈ��0� ¡EO*��JOÅ2ÇðP"FLVdò>ð·!NÏQRS;M!TÚUV#�Wëzdò"EX�]YNZ[V\WX�]!ë�ÅEMH"

^_0�ÅÆ‘=!O.0�MH*ºa�0b" cnÀ.de*~fgø*0�òóhi?X��0�*òóMH!jkSlm0� ¡*0bXn+Q¯ho*òóhir,"

��k��

Page 4: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

GE DH I E D !

)*

GpqrsÅEm #$ tu "$ vwx!yÚ &$ ‘v*z{³±!\y]^9:;kcrsÐ|r}aN*ck~n#GpqÐ|\y�]�n5¦V���!uv��ª^Gp�Ó}S�*Ö×!�Q¯�9:;à�*«jxy!�5�çè�9:;à�*³±#�9��!GpqrsF9:;à�r��*-�\y�Ú� "$)#

Gpq��ÝÓª^5c9:;C¹º<=*�3d)��!����_‘<=cù*ð3��#Gpq�(�n=VW*ck!�eg5‘Gcw��S¡�Gpq*V��æ!u-�E��J�Èk0c¸<*V�0�¹8��Sº#ÈEX^GpqV�0�* ¡O�nz*��-��#ÈE��� *+,- � *��ų!TÚ¡�E¢Ï¼£¤d¥�DE¢*]¦!5TÚø*E¢����8]¦*§��¨êë#V�*¡9���E¢"ÝÓ"E¢!©ªcù*«¢"¬­-®¯*XY!°cùXñ�² *+,- ��³*� "�Ó-ý{��*Ö×!́ Bcù�Q�²��!Kµ¶·FC7��-¸ª*Z[#

^��0cØÙ-V�Øٹٺ»¼½!ÈEFES*º�Gpq¾#rð�� ,./012134 -56 -70870 #$$$ ª^V�e¿!À¾#~ÁÂ"V�L yÃ/�Ä#u8mGpq��úû*Q=¤!ÅXÆy�²Gpq*��úû!ÇXÈÉ£¤ÊQ *+,- rsNb*ËÌr#ÈE"Ím´B£¤�² *+,- *�ÈΪÝÓ!}ÃÏ�²��*� !B�nÈGpq¾#*��E#�~!Û­£¤@N8Gpq¾#*¸ªS¹ÐÑ*�²!>~�D¾#*´BF!-¹ºÀÁÒÓ#

ÈE�^�¸�Èk0cr*GpqVWs¥òó*!�~FØÙ¤ÜJÔÕÈk0c*0cØÙ,¤ÜÖn×*V�ØÙ!ØmÈE*V�ØÙ>~Ùc��!>~¸�ÚÛnÈÈkGpq0�#�~FÖn×rÜÝ8V�rÞC*úûbª�ßà!(YÖjV�F¤ÜV�GpCE¢*áâÚ�r!̂ �ãäE¢Y*ÈM!8ÞC*úûb7åèn(*²æ!~´B�² *+,- *�ÈΪÝÓ!}ÃÏ�²��*� #ÈEç

Page 5: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

"!!!!mn���������

è�V�12!}BXéwÈk*Gpq��0�!Û­£¤@N8Gpq¹��*cê!ë@NnÈÈkGpq��0�#

ÆE¥^ 9×#ì !×í8Èk0cr -56 îïÞC*�S�Èúûbè\V�ØÙ¤Ü!ì #%(×¥µð��r*�ñ@òM"*Æ@òM"}³òM"Gpóô�õö/��úûè\V�ØÙ¤Ü!ì "×8ÛÛè\@X÷�è\øâ!ì 9×í8ùÏúûè\V�ØÙ¤Ü#ÖjV��SêP!>~èn(÷�£¤8¹ºúû*úí��#

ÈE¹ºIûüFÃÄ5cÅÆ6ÒÓJ!FÂýY*F!r!/1:7 eþÂÿüÈE«×æÞC*w!!:;4;<;27 eþÂ�ÈE"�*V�Gpq-V�Gp#£¤>~ÝTÚ#£ÈE$þ =�²Gpq*%&’(!(YTÚ$þ +) V���!}*Gpq’(-Gp+¼¼Q¯�³*9:;r#Û­,��ÂýF!r*w!!>~TÚN-¥./r*,F!-0ÂÂ*,12-34ðñw!F!è\��#

òónÈß�Èk0c*V�0�}µº‘!Ømª¤de�5!Jäe*�¨X�Æ6¯!ë@NVW÷�%¶·8mÈE*B7�89~?!�:;£¤-<$¦�»ê#£¤* ¿"õ<>~TÚ³=>?!¯ .224>? @;.AB2C2DB7:DB/> 0 .224C? @;.AB2C2DB7:DB/>�ho@�#

ÈEØAB-AC�X!DEe"FË"FGH"IJK"L>M"NOP/���s¥ØÙ*òó#FÈEòóÚ�r!ë�D�#ØQGpq¹º0�/ER!FSn}T²Uõ#

!!")**2´1)µº�»��

Page 6: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

7 E . 5H . 5$

+,

�"�!-./�� 1**********************

()1(1!$%¼½ 1********************()1()!$%¾¿ +********************()1(0!$%À¯ ),*******************()1(,!Á¢ 0**********************()1(2!$%NO 0/*******************

()1(+!ÂÃyÂÄ’ÅÆ ,)***************efj7 2*************************

�$�!mn�e����� 2/*****************

())(1!(ÇKLM 2A******************

())()!iÈKLM +)******************())(0!ÉÊ˼½KLM /****************())(,!Ì4Í /+********************efj7 /B************************

�(�!mn�e����� A)*****************

()0(1!ÉÊÎHUÏÐ A0****************

()0()!Ë?ÑÒNO A/*****************()0(0!Á¢�O�Ë?ÑÒNOÓ’ÔÉ B+********efj7 BA************************

�0�!mn��v 1**********************

(),(1!$%&$JKLJK�ÕÖ’¼½ 1*************

(),()!$%&$JKLJK)***ÕÖU× 1*+***********(),(0!$%&$JKLJK)***ÕÖUØÙÚÐ 11)********

Page 7: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

#!!!!mn���������

!!(),(,!×ÛÜUÝ× 11B***********************

!!(),(2!$%&$JKLJK)***’ÕÖUÕÖÞß 1),*************

!!efj7 10*******************************

�)�!mn�e� ¡¢£ 101***********************

()2(1!$%&$JKLJK$%&’ST 101*****************()2()!$%&$JKLJK$%&’cà 10A*****************()2(0!IJ$%&’STUVW 1,0******************efj7 1,+******************************

�#�!�¤¥¦ 1,/****************************

()+(1!¾áÕÖ’â\~ã 12*********************()+()!¾áÕÖâ\’J~äå 12+******************()+(0!æɾ¿çNÍ 1+)**********************()+(,!æÉÕèé¾Í 1+,**********************()+(2!æÉMXê-Í 1/***********************()+(+!æÉ×ê-ëì 1/0**********************()+(/!íîÔÉ 1/A*************************efj7 1A0******************************

�%�!§¨©ª 1A,****************************

()/(1!uïZ; 1A,*************************()/()!ðñZ;’òÉ 1A/**********************()/(0!óôZ; 1AB*************************()/(,!õöZ;’÷ø 1B1**********************()/(2!ùúZ; 1B0*************************()/(+!û�übZ;’ýþ 1B2********************()/(/!Z;Uû’ÿ{¾¿ 1BB********************()/(A!!"D# )*)*************************()/(B!_îZ; )*/*************************efj7 )1*******************************

«¬1!��mn­®¯° )1+************************

«¬2!��­®±²�³ )1/************************

Page 8: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

书书书

SQL语言

C H A P T E R 1

第1章

SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,是一种介于关系代数 与 关 系 演 算 之 间 的 语 言,其 功 能 包 括 数 据 查 询(data

query)、数据操纵(datamanipulation)、数据定义(datadefinition)和数据控

制(datacontrol)四个 方 面,是 一 个 通 用 的、功 能 极 强 的 关 系 数 据 库 语 言。目前,SQL语言已经成为关系数据库的标准语言。

SQL具有高度综合统一、高度非过程化、采取面向集合操作方式、支持

三级模式结构、具有一种语法两种使用方式、结构简洁、易学易用的特点,所以为广大用户和业界所接受,成为国际标准。

SQL支持关系数据库三级模式结构。其中,内模式对应于存储文件,模式对应于基本表,外模式对应于视图(view)。

存储文件的逻辑结构组成了关系数据库的内模式,存储文件的物理文

件结构是透明的。基本表是本身独立存在的表,在SQL中,一个关系就对

应一个表。一些基本表对应一个存储文件,一个表可以带若干索引,索 引

也存放在存储文件中。视图是从基本表或其他视图中导出的一个虚表。在数据库中只存放视

图的定义而不存放视图对应的数据。用户可以用SQL对视图和基本表进行

查询。在用户看来,视图和基本表都是关系,而存储文件对用户是透明的。

SQL的基本功能包括数据定义功能、数据查询功能、数据更新功能、视图管理功能和数据控制功能等五个方面。其中,数据 查 询 是SQL的 最 主

要功能。

实验1.1 数据定义

1. 实验目的

  熟悉SQL的数据定义语言,能够熟练地使用SQL语句来创建和更改

基本表,创建和取消索引。

Page 9: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

2    数据库系统实验指导教程

2. 原理解析

SQL的数据定义包括基本表的定义、索引的定义和视图的定义三部分。这里的“定

义”包括创建(create)、取消(drop)和更改(modify)三部分内容,如表1.1所示。在本实验

中,主要介绍基本表和索引的定义,视图部分的内容将在实验1.4视图中讨论。

表1.1 SQL的数据定义语句

操作对象 创  建   取  消  更  改  

表 CREATETABLE DROPTABLE ALTERTABLE视图 CREATEVIEW DROPVIEW索引 CREATEINDEX DROPINDEX

(1)SQL的基本数据类型。

SQL在定义表的各个属性时,要求指明其中的数据类型和长度。不同的DBMS支持

的数据类型不完全一样。表1.2列举了 MicrosoftSQLServer支持的主要数据类型。

表1.2 MicrosoftSQLServer支持的主要数据类型

序号 符  号 数据类型 说  明

1 TINYINT 整数类型 其值按1个字节存储

2 SMALLINT 整数类型 其值按2个字节存储

3 INTEGERorINT 整数类型 其值按4个字节存储

4 REAL 实数类型 其值按4个字节存储

5 FLOAT 实数类型 其值按8个字节存储

6CHARACTER(n)or

CHAR(n)长度为n的字符类型 1个字符占1个字节

7 VARCHAR(n)最大长度为n的

变长字符类型所占空间与实际字符数有关

8 DATETIME 日期时间类型默认格式为 MMDDYYYY,

HH:MM:AM/PM

9 TIMESTAMP 时间戳更新或 插 入 一 行 时,系 统 自

动记录的日期时间类型

(2)SQL语句用CREATE语句来创建基本表,它的一般形式是:

犆犚犈犃犜犈犜犃犅犔犈<表名>(<列名>,<数据类型>[列级完整性约束条件][,<列名>,<数据类型>[列级完整性约束条件]]…[,<表级完整性约束条件>])

其中,[]内的内容是可选项,<表名>是所要定义的基本表名称。在使用CREATE语 句 创 建 基 本 表 时,必 须 指 定 基 本 表 的 表 名、每 个 列 名 和 数 据 类

型,而列级的完整性约束则是备选的内容,可以指定,也可以省略。对主键和外键等的定

义,一般是在基本表中各个列属性的定义之后出现。各个列的数据类型必须与表1.1中

的数据类型相匹配,不然会产生编译错误,不能运行。

Page 10: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

3    第1章 SQL语言

在定义外键的时 候,要 用 保 留 字 REFERENCES来 指 出 外 键 来 自 的 表 名,即 主 表。

可以通过使用引用完整性任选项ONDELETE,来指出主表中被引用主属性被删除时,

在引用表中的数据的处理方式。为了保证完整性,可能的采取方法有如下三种:

① 选用RESTRICT选项,表明被基本表所引用的主属性不得删除。

② 选用CASCADE选项,表明若主表中删除被引用的主属性,则基本表中引用该外

键的对应行应随着被删除。

③ 选用SETNULL选项,表明若主表中删除被引用的主属性,则基本表中引用该外

键的对应行中的该属性值会被置空。当然,这必须具有一个前提,即该属性在前面说明应

该没有NOTNULL限制。(3)在更改表的时候,可以使用ALTER语句增加新的列,或修改列的数据类型,取

消完整性约束。更改的过程是将列作为一个对象来进行。例如修改列的数据类型,则在

整个表中,所有元组的这个列的取值的数据类型都发生改变。标准SQL没有提供直接的

删除列的功能,当需要删除列的时候,必须通过间接步骤来实现。

ALTER语句更改表的语句的一般格式如下:

犃犔犜犈犚犜犃犅犔犈<表名>[犃犇犇<新列名><数据类型>[完整性约束条件]]

[犇犚犗犘犆犗犖犛犜犚犃犐犖犜[完整性约束名]]

[犃犔犜犈犚犆犗犔犝犕犖<列名><数据类型>]

其中,ADD子句用于增加新列和新的完整性约束条件;DROPCONSTRAINT子句用于

取消完整性约束条件;ALTERCOLUMN子句用于更改原有的定义,包括更改列名和数

据类型。(4)取消表的语句格式:

犇犚犗犘犜犃犅犔犈<表名>

(5)创建索引的语句格式:

犆犚犈犃犜犈[犝犖犐犙犝犈][犆犔犝犛犜犈犚犈犇]犐犖犇犈犡<索引名>

犗犖<表名>(<列名>[<排序方式>][,<列名>[,<排序方式>]]…)

(6)取消索引的语句格式:

犇犚犗犘犐犖犇犈犡表名.索引名

3. 实验内容

本实验的主要内容包括:

使用CREATE语句创建基本表。

更改基本表的定义,增加列,删除列,修改列的数据类型。

创建表的升降序索引。

取消表、表的索引或表的约束。

Page 11: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

4    数据库系统实验指导教程

4. 实验步骤

1)要求

(1)使用SQL语句创建关系数据库表:人员表PERSON(P#,Pname,Page)、房间

表ROOM(R#,Rname,Rarea)、表PR(P#,R#,Date)。其中:P#是表PERSON的主

键,具有唯一性约束;Page具有约束“大于18”;R#是表ROOM 的主键,具有唯一性约

束。表PR中的P#、C#是外键。(2)更改表PERSON,增加属性Ptype(类型是CHAR,长度为10),取消Page大于

18的约束。把表ROOM中的属性Rname的数据类型改成长度为40。(3)删除表ROOM的一个属性Rarea。(4)取消表PR。(5)为ROOM表创建按R#降序排列的索引。(6)为PERSON表创建按P#升序排列的索引。(7)创建表PERSON的按Pname升序排列的唯一性索引。(8)取消PERSON表P#升序索引。

2)分析与解答

(1)使用SQL语句创建关系数据库表:人员表PERSON(P#,Pname,Page)、房间

表ROOM(R#,Rname,Rarea)、表PR(P#,R#,Date)。其中:P#是表PERSON的主

键,具有唯一性约束;Page具有约束“大于18”;R#是表ROOM 的主键,具有唯一性约

束。表PR中的P#、C#是外键。在实际情况中,可以使用下列的SQL语句来实现上述功能(语句或代码参见网站提

供的1_1_1.sql代码文件,简称“见1_1_1.sql”,下同):

犆犚犈犃犜犈犜犃犅犔犈犘犈犚犛犗犖(犘#犆犎犃犚(8)犖犗犜犖犝犔犔犝犖犐犙犝犈,

犘狀犪犿犲犆犎犃犚(20)犖犗犜犖犝犔犔,

犘犪犵犲犐犖犜,

犘犚犐犕犃犚犢犓犈犢(犘#),犆犎犈犆犓(犘犪犵犲>18))

犆犚犈犃犜犈犜犃犅犔犈犚犗犗犕(犚#犆犎犃犚(8)犖犗犜犖犝犔犔犝犖犐犙犝犈,

犚狀犪犿犲犆犎犃犚(20),

犚犪狉犲犪犉犔犗犃犜(10),

犘犚犐犕犃犚犢犓犈犢(犚#))

犆犚犈犃犜犈犜犃犅犔犈犘犚(

犘#犆犎犃犚(8)犖犗犜犖犝犔犔犝犖犐犙犝犈,

犚#犆犎犃犚(8)犖犗犜犖犝犔犔犝犖犐犙犝犈,

犇犪狋犲犇犪狋犲狋犻犿犲,

犘犚犐犕犃犚犢犓犈犢(犘#,犚#),

Page 12: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

5    第1章 SQL语言

犉犗犚犈犐犌犖犓犈犢(犘#)犚犈犉犈犚犈犖犆犈犛犘犈犚犛犗犖犗犖犇犈犔犈犜犈犆犃犛犆犃犇犈,

犉犗犚犈犐犌犖犓犈犢(犚#)犚犈犉犈犚犈犖犆犈犛犚犗犗犕犗犖犇犈犔犈犜犈犆犃犛犆犃犇犈)

(2)更改表PERSON,增加属性Ptype(类型是CHAR,长度为10),取消Page大于

18的约束。把表ROOM中的属性Rname的数据类型改成长度为40。可以使用下列的SQL语句来更改表(见1_1_2.sql):

犃犔犜犈犚犜犃犅犔犈犘犈犚犛犗犖犃犇犇犘狋狔狆犲犆犎犃犚(10)

犃犔犜犈犚犜犃犅犔犈犘犚犚犛犗犖犇犚犗犘犆犗犖犛犜犚犃犐犖犜犆犓_犘犈犚犛犗犖_犘犪犵犲_78犅3犈犉犆犃犃犔犜犈犚犜犃犅犔犈犚犗犗犕犃犔犜犈犚犆犗犔犝犕犖犚狀犪犿犲犆犎犃犚(40)

注意:在取消约束的时候,应该写出约束的名 称。当 定 义 约 束 的 时 候,虽 然 用 户 没

有指定约束的名称,但 是 在 数 据 库 会 为 这 个 约 束 起 一 个 名 称(不 同 的 时 候 创 建 名 称 可

能不同)。要查看这个 名 称,可 以 通 过 在 企 业 分 析 器 中,查 看 设 计 表 中 的 约 束 选 项,可

以看 到 这 个 约 束 的 名 称。在 本 实 验 中,这 个 约 束 的 名 称 为 CK_PERSON_Page_

78B3EFCA。(3)删除表ROOM的一个属性Rarea。在SQLServer2000中,提 供 了 删 除 列 的 操 作,可 以 使 用 下 列 的SQL语 句 实 现

(见1_1_3.sql):

犃犔犜犈犚犜犃犅犔犈犚犗犗犕犇犚犗犘犆犗犔犝犕犖犚犪狉犲犪

在其他数据库支持的SQL中,不是都有直接删除列的语句的。当SQL不支持直接

删除列操作的时候,要实现这一个操作,可以通过先把表删除,再重新定义符合要求的新

表这两步的操作来完成。在这个过程中,删除表会导致数据的丢失,所以在删除表之前,需要复制一份原表的数据和定义用以保存。在定义了新的符合要求的表后,再把表的数

据导入新的表中。(4)取消表PR。取消表,直接使用DROP语句(见1_1_4.sql):

犇犚犗犘犜犃犅犔犈犘犚

(5)为ROOM表创建按R#降序排列的索引(见1_1_5.sql)。创建索引使用CREATE语句,需要指出索引的名称。

犆犚犈犃犜犈犐犖犇犈犡犡犆犖犗犗犖犚犗犗犕(犚#犇犈犛犆)

(6)为PERSON表创建按P#升序排列的索引(见1_1_6.sql)。

犆犚犈犃犜犈犐犖犇犈犡犡犛犖犗犗犖犘犈犚犛犗犖(犘#)

在本题中,创建的索引的名称是XSNO,在这里列名的排列方式省略了。在SQL语

句中,排列方式只有两种ASC(升序)和DESC(降序),默认值为ASC。所以,本题排列的

方式是升序。(7)创建表PERSON的按Pname升序排列的唯一性索引。

Page 13: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

6    数据库系统实验指导教程

在前面的创建索引中,所有的保留字UNIQUE和CLUSTER都被省略了,这时取默

认值CLUSTER。当需要建 立 唯 一 性 索 引 的 时 候,UNIQUE就 不 能 省 略。例 如 下 面 的

SQL语句创建表PERSON的索引RNUA(见1_1_7.sql):

犆犚犈犃犜犈犝犖犐犙犝犈犐犖犇犈犡犚犖犝犃犗犖犘犈犚犛犗犖(犘狀犪犿犲犃犛犆)

(8)取消PERSON表P#升序索引。所要求取消表PERSON的索引XSNO的SQL语句如下(见1_1_8.sql):

犇犚犗犘犐犖犇犈犡犘犈犚犛犗犖.犡犛犖犗

说明:在 MicrosoftSQLServer中进行SQL语句的实验,可以采取下列步骤:

打开企业管理器,双击选定的数据库。在菜单“工具”中,单击“SQL查询分析器”,打开查询分析器。

在查询分析器中输入相应的SQL语句。

按F5键或是单击按钮执行。

5. 习题

(1)创 建 数 据 库 表 CUSTOMERS(CID,CNAME,CITY,DISCNT)、数 据 库 表

AGENTS(AID,ANAME,CITY,PERCENT)、数 据 库 表PRODUCTS(PID,PNAME)。其中,CID、AID、PID分别是各表的主键,具有唯一性约束。

(2)创建数据库表ORDERS(ORDNA,MONTH,CID,AID,PID,QTY,DOLLARS)。其

中,ORDNA是主键,具有唯一性约束。CID、AID、PID分别是外键引用自表CUSTOMERS、表AGENTS,表PRODUCTS。

(3)增加数据库表PRODUCTS三个属性列:CITY、QUANTITY、PRICE。(4)为以上四个表建立各自的按主键增序排列的索引。(5)取消(4)建立的四个索引。试一试:动 手 在 自 己 的 机 器 的SQLServer中 进 行 直 接 删 除 表PRODUCTS的 列

CITY的操作,看看你的SQLServer的版本是否支持这一操作?

实验1.2 数据查询

1. 实验目的

  熟悉SQL语句的数据查询语言,能够使用SQL语句对数据库进行单表查询、连接查

询、嵌套查询、集合查询和统计查询。

2. 原理解析

SQL提供了SQL映像语句用于数据库查询。将一个子查询定义为SUBQUERY,可以得到如下的查询语句的一般形式:

(1)子查询:

Page 14: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

7    第1章 SQL语言

犛犝犅犙犝犈犚犢∷=犛犈犔犈犆犜[犃犔犔|犇犐犛犜犐犖犆犜]<目标列表达式> [别名][,<目标列表达式> [别名]]…

犉犚犗犕<表名或视图名> [别名][,<表名或视图名> [别名]]…[犠犎犈犚犈<条件表达式>][犌犚犗犝犘犅犢<列名1>[,<列名2,>]…][犎犃犞犐犖犌<条件表达式>]]

(2)查询表达式:

犛犈犔犈犆犜犛犜犃犜犈犕犈犖犜∷=犛犝犅犙犝犈犚犢|犛犝犅犙犝犈犚犢犝犖犐犗犖[犃犔犔]|犐犖犜犈犚犛犈犆犜[犃犔犔]|犈犡犆犈犘犜[犃犔犔]]犛犝犅犙犝犈犚犢[犗犚犇犈犚犅犢(<列名1> [犃犛犆|犇犈犛犆][,<列名2>][犃犛犆|犇犈犛犆]…)]

在子查询SUBQUERY中,SELECT、FROM 和 WHERE这三个保留字分别带三个

子句,构成了SQL语句中最基本的查询语句:

(1)SELECT子句表示查询的目标属性。

(2)FROM子句表示查询所涉及的关系。

(3)WHERE子句表示查询的逻辑条件。

由SELECT子 句、FROM 子 句 和 WHERE子 句 构 成 的 映 像 语 句 的 含 义 是:将

FROM子句指定的基本表或视图做笛卡儿积;再根据 WHERE子句的条件表达式,从中

找出满足条件的元组;最后根据SELECT子句中的目标列表达式形成结果表。

GROUP子句的作用是将元组按后面所跟的列名1的值进行分组,属性列中将值相

等的元组 分 成 一 个 组,每 个 组 在 结 果 表 中 产 生 一 个 记 录。如 果 GROUP子 句 带 有

HAVING子句,则表示只有满足 HAVING子句指定的条件的元组才能在结果关系表中

输出。如果有ORDER子句,则结果还要按列名2的值的升序(ASC)或降序(DESC)的排

列次序输出。

上述形式中的目标列表达式可以有如下两种:

(1)[<表名>.]或。

(2)[<表名>.]<属性列名表达式>[,[<表名>.]<属性列名表达式>]…。

其中,<属性列名表达式>是由属性列、作用于属性列的集函数和常量的任意算术运

算(+,-,,/)组成的运算公式。

上述一般形式中的条件表达式的形式如下(其中θ是比较运算符):

(1)<属性列名>θ<属性列名>。

(2)<属性列名>θ<常量>。

(3)<属性列名>θ[ANY|ALL](SELECT语句。)

(4)<属性列名> [NOT]BETWEEN<常量> AND<常量> 。

(5)<属性列名> [NOT]IN(<值1>[,<值2>]…)。

(6)<属性列名> [NOT]IN(SELECT语句)。

(7)<属性列名> [NOT]LIKE<匹配串>。

(8)<属性列名>IS[NOT]NULL。

Page 15: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

8    数据库系统实验指导教程

(9)[NOT]EXISTS(SELECT语句)。

(10)条件表达式 AND|OR条件表达式[AND|OR条件表达式]…。

(11)NOT<条件表达式>。

(12)常量θ<集合函数>。

注意:(12)只能用在 HAVING的条件表达式中。

由上面的目标表达式和条件表达式的多种不同形式,SQL语句可以提供多种数据查

询方式。

1)单表查询

使用SELECT语句做单表查询时,若在SELECT后面只指定输出某些列时,可以只

查询表中的这些列;若SELECT后面跟,则可以查询所有列;若SELECT后面是带列

名的算术表达式,可以查询经过计算之后的值。而且,用户还可以通过别名来改变查询结

果的列标题,这对于含算术表达式、常量、函数名的目标列表达式尤为有用。

(1)使用DISTINCT。

当 需 要 消 除 在 查 询 结 果 中 的 重 复 行 的 时 候,可 以 使 用 保 留 字 DISTINCT。

DISTINCT的对应的选项是ALL,默认值是ALL。所以,当要显示重复行的时候,ALL可以 写 也 可 以 省 略;而 要 消 除 重 复 行 的 时 候,则 DISTINCT 一 定 要 写 出 来。使 用

DISTINCT的时候要注意它保持的唯一性是对SELECT后的列名所组成的新的元组的

唯一性,而不是各个单独的列取值或原来元组(没有经过SELECT子句映射的原来基本

表中的元组)的唯一性。

(2)使用 WHERE子句中条件的运算符。

在 WHERE子句中,可以用下列方法来表示结果元组必须满足的条件:

使用常见的比较符号=、>、<、>=、!= 、<>、!>、!< 、=、= 来做比较;

使用BETWEENAND、NOTBETWEENAND来指定范围;

使用IN、NOTIN来查找属性值属于或不属于指定集合;

使用谓词LIKE、NOTLIKE来查找匹配的元组;

使用ISNULL查找属性值为空值的元组;

使用逻辑运算符AND和OR可连接多个查询条件。如果这两个运算符同时出现

在同一个 WHERE条件子句中,则AND的优先级高于OR,但用户可以用括号改

变优先级。

(3)使用ORDERBY对查询结果排序。

用户可以用ORDERBY子句指定按照一个或多个属性列的升序或降序重新排列查

询结果,其中升序为默认值。

(4)使用集函数。

SQL提供集函数进行查询,可以直接返回查询的一些统计结果,方便地统计需要得

到的数据。SQL函数是一种综合信息的统计函数,包括计数,求最大值、最小值、平均值、

和值等。SQL函数一般是以列标识符出现在SELECT子句的目标列中,也 可 以 出 现 在

Page 16: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

9    第1章 SQL语言

HAVING子句的条件中。在SQL查询语句中,如果有GROUPBY分组子句,则语句中

的函数为分组统计函数;如果没有GROUPBY分组子句,则语句中的函数为全部结果集

的统计函数。基本的SQL函数的名称和功能详见于表1.3。

表1.3 基本的SQL集函数

函  数 功  能

AVG(<数值表达式>) 求与字段相关的数值表达式的平均值

SUM(<数值表达式>) 求与字段相关的数值表达式的和值

MIN(<字段表达式>) 求字段表达式的最小值

MAX(<字段表达式>) 求字段表达式的最大值

COUNT(|<字段>) 求记录行数(),或求不是NULL的字段的行数

(5)使用GROUPBY进行分组。

GROUPBY子句可以将查询结果表的各行按一列或多列取值相等的原则进行分组。

对查询结果分组的目的是为了细化集函数的作用对象。如果未对查询结果分组,集函数

将作用于整个查询结果,即整个查询结果只有一个函数值。否则,集函数将作用于每一个

组,即每一组都有一个函数值。

如果分组后还要求按一定的条件对这些组进行筛选,最终只输出满足指定条件的组,

则可以使用 HAVING短语指定筛选条件。

注意:

SELECT子句中如果有集合函数,则不允许出现包含其他字段的表达式,但如果

有GROUPBY子句,则允许GROUPBY子句出现的字段。

如果在查询语句中有GROUPBY子句,则SELECT子句不允许出现包含其他字

段的表达式,允许GROUPBY子句出现的字段和集合函数表达式。

2)连接查询

(1)不带谓词连接和等值连接。

在连接运算中有如下两种情况:第一种是做笛卡儿乘积连接,它是不带连接谓词的

连接,对两个表中元组作交叉乘积,这就意味着其中一表中的每一元组都要与另一表中的

每一元组作拼接,因此结果表往往很大;第二种是自然连接,按照两个表中的相同属性进

行等值连接,且目标列中去掉了重复的属性列,但保留了所有不重复的属性列。具体的操

作一般都是使用自然连接,很少使用笛卡儿乘积的连接。(2)自连接。

自连接是连接的另外一种特殊的情况,它是对自身的连接。在具体的使用中,一般是

采用表别名的实行来实现自连接。(3)外连接。

在通常的连接操作中,只有满足连接条件的元组才能作为结果输出。有时可能不满

足连接条件但仍需要输出其中一个表的信息时,可以使用外连接(OuterJoin)。(4)复合条件连接。

Page 17: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

10   数据库系统实验指导教程

复合条件连接是指 WHERE语句的条件不只一个而是有多个条件的连接情况。

(5)多表连接。

连接操作除了可以是两表连接、一个表与其自身连接外,还可以是两个以上的表进行

连接,即多表连接。

3)嵌套查询

嵌套查询就是指一个查询语句中 WHERE子句的逻辑条件含有另一个查询语句的

情况。因为查询语句的结果是一张表,表就是元组的集合,因此可以将 WHERE子句内

的查询语句看作是一个集合。WHERE子句所涉及的逻辑条件,也就可以转化为“元素x与集合S”或“集合S1与集合S2”之间的关系表示。

(1)对于子查询有如下限制:

不能使用ORDERBY子句。

外层SELECT语句的变量可以用在子查询中,反之则不行。(2)不相关子查询和相关子查询。

这两种子查询不同之处在于:不相关子查询的子查询是在没有接受任何输入数据的

情况 下 向 外 层 的SELECT 语 句 传 递 一 个 行 集,即 内 层 的 子 查 询 完 全 独 立 于 外 层 的

SELECT语句;而相关子查询要使用外层SELECT语句所提供的数据。

由于对外层的依赖性不同,这两种子查询的处理方法也有如下不同:

① 不相关子查询:是由里向外逐层处理。即每个子查询在上一级查询处理之前求

解,子查询的结果用于建立其父查询的查找条件。当查询涉及多个表时,使用嵌套结构逐

次求解,就可以将复杂的问题转化为多个相对简单的查询,使得语句层次分明。这恰好是

SQL的结构化的反映。有些嵌套查询可以用连接运算替代,但对于多表查询来说,嵌套

查询的执行效率比连接查询效率要高。这在后面的具体实验中将得到验证。

② 相关子查询:首先取外层查询中表的第一个元组,根据它与内层查询相关的属性

值处理内层查询,若 WHERE子句返回值为真,则取此元组放入结果表;然后再取外层

表的下一个元组;重复这一过程,直至外层表全部检查完为止。

(3)连接子查询使用的谓词。

第一类,使用带有IN的谓词的子查询。它的一般格式是:

犲狓狆狉[犖犗犜]犐犖(狊狌犫狇狌犲狉狔)|犲狓狆狉[犖犗犜]犐犖(狏犪犾{,狏犪犾… })

第二类,带有比较运算符的子查询。当能确切知道内层查询返回单值时,可用比较运

算符(>、<、=、>=、<=、!=或< >)。需要注意的是,当内层返回的不是单值时,会

引起编译上的错误,不能执行。同时,这类运算符可以和SOME、ANY配合使用。一般格式如下:

犈犡犘犚θ{犛犗犕犈|犃犖犢|犃犔犔}(犛犝犅犛犙犝犈犚犢)

θ犐犛犛犗犕犈犗犘犈犚犜犈犚犐犖犜犎犈犛犈犜{<,<=,=,<>,>,>=}

第三类,带有SOME、ANY或ALL谓词的子查询。谓词SOME/ANY的语义是任意一

个值,谓词ALL则是表示所有值。这类谓词需要配合使用比较运算符,如表1.4所示。

Page 18: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

11   第1章 SQL语言

表1.4 比较运算符的谓词含义

比较运算符号 含  义

> ANY 大于子查询结果中的某个值

> ALL 大于子查询结果中的所有值

< ANY 小于子查询结果中的某个值

< ALL 小于子查询结果中的所有值

>= ANY 大于等于子查询结果中的某个值

>= ALL 大于等于子查询结果中的所有值

<= ANY 小于等于子查询结果中的某个值

<= ALL 小于等于子查询结果中的所有值

= ANY 等于子查询结果中的某个值

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

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

ANY和ALL谓词有时也可以用集函数实现,它们的对应关系如表1.5所示。用集

函数实现子查询通常比直接用ANY或ALL查询效率要高,因为前者通常能够减少比较

次数。

表1.5 ANY与ALL与集函数的对应关系

谓词 = <>或!= <   <=  >  >= 

SOME|ANY IN <MAX <=MAX >MIN >=MINALL NOTIN <MIN <=MIN >MAX >=MAX

第四类,带有EXISTS谓词的子查询,它的一般格式是:

[犖犗犜]犈犡犐犛犜犛(犛犝犅犛犙犝犈犚犢)

带有EXISTS谓词的子查询不返回任何数据,只产生逻辑真值TRUE或逻辑 假 值

FALSE。若内层查询结果非空,则返回真值;反 之,则 返 回 假 值。由EXISTS引 出 的 子

查询,其目标列表达式通常都用,因为带EXISTS的子查询只返回真值或假值,给出列

名没有实际意义。当然,如果非要写出列名,也是允许的,在SQLServer中不会产生语法

错误。注意:一些带EXISTS或NOTEXISTS谓词的子查询不能被其他形式的子查询等

价替换;而所有带IN谓词、比较运算符、ANY和ALL谓词的子查询都能用带EXISTS谓词的子查询等价替换。

使用EXISTS/NOTEXISTS实现全称量词。SQL中没有全称量词(FORALL),可以把带有全称量词的谓词转换为等价的带有存在量词的谓词:

(x)P≡┐(x(┐P))这样转换得到的等价形式,给出了这类问题的求解方式。也就是说,如果所面临的查

询要求被检索的对象集合必须符合某个带有“所有”这类关键词的条件,可以按照下列步

骤来执行:

Page 19: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

12   数据库系统实验指导教程

① 为要检索的对象命名并考虑如何表述要检索的候选对象的一个反例。这个反例

刚好是不符合前面提到的“所有”对象规定的条件。

② 建立SELECT语句的搜索条件以选出步骤①所创建的所有反例。

③ 建立包含步骤②所创建的语句的搜索条件,说明不存在步骤①定义的那种反例,

一般用到NOTEXISTS谓词。另外,使用EXISTS/NOTEXISTS还能够实现差运算,例如在实验步骤中的查询没

有学生选的课程的编号。这就是用NOTEXISTS实现的一个差运算。

4)集合运算

在SQL语句中可以使用集合运算符 UNION、INTERSECT和EXCEPT来实现 集

合运算或、交和减。一般格式是:

犛犝犅犙犝犈犚犢{犝犖犐犗犖[犃犔犔]|犐犖犜犈犚犛犈犆犜[犃犔犔]|犈犡犆犈犘犜[犃犔犔]}犛犝犅犙犝犈犚犢

当其中的保留字ALL省略时,运算的结果会自动地把重复的 结 果 项 去 掉。当 需 要

显示重复项的时候,要写上ALL。

例如:当在R表中有值A出现5次,S表中值A出现3次。(1)若做UNION运算后,去掉重复项,只出现1次;若使用UNIONALL,则会出现

8次。(2)若做INTERSECT运算,只出现1次;若使用INTESECTALL,则出现3次。(3)若做EXCEPT运算,不出现;若使用EXCEPTALL,则出现2次。

事实上,在 MicrosoftSQLServer2000,以及很多的SQLServer中,只支持UNION运算符,而不支持INTERSECT和EXCEPT运算符。在具体的实验中,必须使用其他形

式进行代替。

3. 实验内容

实验内容主要是对数据库进行查询操作,包括如下四类查询方式:(1)单表查询。

查询的目标表达式为所有列、指定的列或指定的列的运算三种不同。

使用DISTINCT保留字消除重复行。

对查询结果排序和分组。

集合分组使用集函数进行各项统计。(2)连接查询。

笛卡儿连接和等值连接。

自连接。

外连接。

复合条件连接。

多表连接。(3)嵌套查询。

通过实验验证对子查询的两个限制条件。

Page 20: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

13   第1章 SQL语言

体会相关子查询和不相关子查询的不同。

考察四类谓词的用法,包括:第一类,IN、NOTIN;第二类,带有比较运算符的子查询;第三类,SOME、ANY或ALL谓词的子查询,查询最大值和最小值情况;第四类,带有EXISTS谓词 的 子 查 询,实 现“所 有”等 情 况(如 王 宏 修 的“所 有”课

程,“所有”女生选修的课程)。(4)集合运算。

使用保留字UNION进行集合或运算。

采用逻辑运算符AND或OR来实现集合交和减运算。

4. 实验步骤

1)要求

以School数据库为例,在该数据库中存在四张表格,分别为:

表STUDENTS(sid,sname,email,grade);

表TEACHERS(tid,tname,email,salary);

表COURSES(cid,cname,hour);

表CHOICES(no,sid,tid,cid,sorce)。在数据库中,存在这样的关系:学生可以选择课程,一个课程对应一个教师。在 表

CHOICES中保存学生的选课记录。按以下要求对数据库进行查询操作:(1)查询年级为2001的所有学生的名称并按编号升序排列。(2)查询学生的选课成绩合格的课程成绩,并把成绩换算为积点(60分对应积点为

1,每增加1分,积点增加0.1)。(3)查询课时是48或64的课程的名称。(4)查询所有课程名称中含有data的课程编号。(5)查询所有选课记录的课程号(不重复显示)。(6)统计所有老师的平均工资。(7)查询所有教师的编号及选修其课程的学生的平均成绩,按平均成绩降序排列。(8)统计各个课程的选课人数和平均成绩。(9)查询至少选修了三门课程的学生编号。(10)查询编号800009026的学生所选的全部课程的课程名和成绩。(11)查询所有选了database的学生的编号。(12)求出选择了同一个课程的学生对。(13)求出至少被两名学生选修的课程编号。(14)查询选修了编号80009026的学生所选的某个课程的学生编号。(15)查询学生的基本信息及选修课程编号和成绩。(16)查询学号850955252的学生的姓名和选修的课程名称及成绩。

Page 21: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

14   数据库系统实验指导教程

(17)查询与学号850955252的学生同年级的所有学生资料。(18)查询所有有选课的学生的详细信息。(19)查询没有学生选的课程的编号。(20)查询选修了课程名为C++的学生学号和姓名。(21)找出选修课程成绩最好的选课记录。(22)找出和课程UML或课程C++的课时一样课程名称。(23)查询所有选修编号10001的课程的学生的姓名。(24)查询选修了所有课程的学生姓名。(25)利用集合运算,查询选修课程C++或选择课程Java的学生的编号。(26)实现集合交运算,查询既选修课程C++又选修课程Java的学生的编号。(27)实现集合减运算,查询选修课程C++而没有选修课程Java的学生的编号。

2)分析与解答

(1)根据要求,查询年级为2001的所有学生的名称,只需对STUDENTS表做单表

查询。SQL语句如下(见1_2_1.sql):

犛犈犔犈犆犜犛犖犃犕犈

犉犚犗犕犛犜犝犇犈犖犜犛

犠犎犈犚犈犌犚犃犇犈='2001'

犗犚犇犈犚犅犢犛犐犇

注意:当属性列的数值类型是CHAR时,与指定的值作比较,指定的值要加上单引

号(例如本例中的2001就必须加上单引号)。这是比较容易忽视的地方,容易引发错误。

ORDERBY有ASC或DESC两种方式。本题采用了默认的情况,默认值是ASC,也即结

果是按SID的取值升序排列。(2)查询选课成绩合格的学生的课程成绩,并把成绩换算为积点。在本题中,积点这一个属性,在原来的表定义里面是没有的。怎样才能得到这个属性

值呢?从给出的定义中,可以发现积点是可以从表CHOICES中的SCORE属性计算得

到。SELECT子句中可以带列名,也可以是含有标准运算符号的表达式。所以,积 点 这

一个属 性,可 以 用(SCORE-50)/10这 个 式 子 来 表 示,为 增 加 可 读 性,还 可 以 在 结 果 集

中,每行 的 积 点 的 值 前 面 加 上 提 示“POINTOFSCORE:”符 合 要 求 的SQL语 句 如 下

(见1_2_2.sql):

犛犈犔犈犆犜犜犐犇,犆犐犇,犛犆犗犚犈,'犘犗犐犖犜犗犉犛犆犗犚犈',(犛犆犗犚犈-50)/10

犉犚犗犕犆犎犗犐犆犈犛

犠犎犈犚犈犛犆犗犚犈>=60

(3)查询课时是48或64的课程名称,考虑使用IN运算符,匹配的集合是('48','64'),

SQL语句如下(见1_2_3.sql):

犛犈犔犈犆犜犆犖犃犕犈

犉犚犗犕犆犗犝犚犛犈犛

犠犎犈犚犈犎犗犝犚犐犖('48','64')

Page 22: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

15   第1章 SQL语言

注意:48和64都必须加上单撇号。(4)查询所有课 程 名 称 中 含 有data的 课 程 名 称,可 以 使 用 模 糊 匹 配 的 符 号LIKE,

SQL语句如下(见1_2_4.sql):

犛犈犔犈犆犜犆犖犃犕犈犉犚犗犕犆犗犝犚犛犈犛犠犎犈犚犈犆犖犃犕犈犾犻犽犲'%犱犪狋犪%'

查询得到database、datastructure、datamining、datawarehouse这四个值。(5)要查 询 所 有 选 修 记 录 的 课 程 号,考 虑 直 接 查 询 表 CHOICES,SQL语 句 如 下

(见1_2_5.sql):

犛犈犔犈犆犜犆犐犇犉犚犗犕犆犎犗犐犆犈犛

执行之后,可以看到有293441个项,其中有很多的重复选项。怎样去掉这些重复选

项呢?可以使用保留字DISTINCT来解决这个问题。修改后的SQL语句如下(见1_2_

6.sql):

犛犈犔犈犆犜犇犐犛犜犐犖犆犜犆犐犇犉犚犗犕犆犎犗犐犆犈犛

运行之后,就可以得到不重复的所有的课程编号,共49个。使用DISTINCT的时候需要注意一 个 问 题,就 是 当DISTINCT后 面 跟 的 是 多 个 列

名时,DISTINCT保证的唯一性是关于所有列组成的行的唯一性,而不保 证 单 独 的 列 的

取值的唯一性。例如,使用下面语句来进行查询(见1_2_7.sql):

犛犈犔犈犆犜犇犐犛犜犐犖犆犜犆犐犇,犜犐犇犉犚犗犕犆犎犗犐犆犈犛

这时,DISTINCT保证的唯一性,是由CID、TID构成的这个行的唯一性。“(10008,

262241926)”这个行只出现一次,满足唯一性。而对于组成这个行的CID和TID,就不一

定是唯 一 了,在 查 询 结 果 中 还 可 能 出 现 CID=10008而 TID !=262241926的 行,或

TID=262241926而CID!=10008的行,比如(1008,233197726)这个元组。保留字ALL是用来 表 示 与DISTINCT相 对 的 查 询 方 式。当 这 个 保 留 字 默 认 的 时

候,即既不 出 现 ALL也 不 出 现DISTINCT的 时 候,默 认 是 显 示 重 复 选 项 也 就 是 ALL。所以,在SQL语句里面写上ALL跟没写在执行的效果上是一样的。但有些时候,需要增

强可读性时,可以在语句中写上ALL来表示强调。(6)查询所有老师的平均工资,使用SQL函数 AVG()来 求 平 均 数,SQL语 句 如 下

(见1_2_8.sql):

犛犈犔犈犆犜犃犞犌(犛犃犔犃犚犢)犉犚犗犕犜犈犃犆犎犈犚犛

(7)查询所有教师的编 号 和 选 修 其 课 程 学 生 的 平 均 成 绩,按 总 平 均 成 绩 降 序 排 列。考虑使用GROUPBY对所有的选课信息按教师标号分组,再对每个组求平均,即可以得

到平均成绩。所用的SQL语句如下(见1_2_9.sql):

犛犈犔犈犆犜犜犐犇,犃犞犌(犛犆犗犚犈)

Page 23: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

16   数据库系统实验指导教程

犉犚犗犕犆犎犗犐犆犈犛犌犚犗犝犘犅犢犜犐犇犗犚犇犈犚犅犢犃犞犌(犛犆犗犚犈)犇犈犛犆

注意:如果在查询语句中有GROUPBY子句,则SELECT子句不允许出现包含其

他字段的表达式,而允许GROUPBY子句出现的字段和集合函数表达式。例如在本题

中,如果在SELECT子句中出现表CHOICES的其他列名时,会出现语法错误。而TID是GROUPBY子句 出 现 的 字 段,AVG(SCORE)是 集 合 函 数 表 达 式,所 以 可 以 出 现 在

SELECT子句中。还有一个特殊的地方,一旦查询中出现了分组GROUPBY子句,则集

合函数是对组内进行操作。例如在本题中,求平均数AVG不是求所有元组的SCORE的

平均数,而是求每个组中的元组的SCORE的平均数,一个组有一个平均数。(8)统计各个课程的选课人数和平均成绩,可以使用SQL函数中的COUNT()来求

记录 数 和 AVG()来 求 平 均 数。这 里 要 求 是 按 各 个 课 程 统 计,可 以 使 用 按 课 程 分 组

(GROUPBY)来实现,SQL语句如下(见1_2_10.sql):

犛犈犔犈犆犜犆犐犇,犆犗犝犖犜(犖犗),犃犞犌(犛犆犗犚犈)

犉犚犗犕犆犎犗犐犆犈犛犌犚犗犝犘犅犢犆犐犇

(9)查询至少选修了三门课程的学生编号,可以考虑按学生编号进行分组,只要一个

组中包含有三个以上元组,这个组名就是满足查询条件的一个。SQL语句如下(见1_2_

11.sql):

犛犈犔犈犆犜犛犐犇犉犚犗犕犆犎犗犐犆犈犛犌犚犗犝犘犅犢犛犐犇犎犃犞犐犖犌犆犗犝犖犜()>3

通过本题,可以看出 WHERE子句与HAVING短语的根本区别在于作用对象不同。

WHERE子句作用于基本表或视图,从中选择 满 足 条 件 的 元 组。HAVING短 语 作 用 于

组,从中选择满足条件的组。说明:以上(1)~(9)都属于单表查询,简单的查询语句与ORDERBY、GROUPBY、

集合函数等结合使用,可以查询广泛的信息。单表查询在 MicrosoftSQLServer中,还可以使用下面的步骤来进行实验:

打开企业管理器,双击选定的数据库School。

选定需要更新的基本表,单 击 右 键,选 择 快 捷 菜 单 中 的“打 开 表”,单 击“查 询”按

钮,进入SQL语句的编辑界面。

按照提示,写出 WHERE子句,单击“运行”按钮执行。(10)查询编号800009026的学生所选的全 部 课 程 的 课 程 名 和 成 绩,SQL语 句 如 下

(见1_2_12.sql):

犛犈犔犈犆犜犆犗犝犚犛犈犛.犆犖犃犕犈,犆犎犗犐犆犈犛.犛犆犗犚犈犉犚犗犕犆犗犝犚犛犈犛,犆犎犗犐犆犈犛

Page 24: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

17   第1章 SQL语言

犠犎犈犚犈犆犎犗犐犆犈犛.犛犐犇='800009026'

(11)查询所有选了database的学生的编号。这个操作需要对两个表进行连接操作。可以采用等值连接,SQL语句如下(见1_2_13.sql):

犛犈犔犈犆犜犆犎犗犐犆犈犛.犛犐犇犉犚犗犕犆犎犗犐犆犈犛,犆犗犝犚犛犈犛犠犎犈犚犈犆犎犗犐犆犈犛.犆犐犇=犆犗犝犚犛犈犛.犆犐犇犃犖犇犆犗犝犚犛犈犛.犆犖犃犕犈='犱犪狋犪犫犪狊犲'

如果考虑子查询的形式,并由外界SELECT语句向内部传递数据database的话,那

么本查询也可以使用下面的语句得到(见1_2_14.sql):

犛犈犔犈犆犜犛犐犇犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈'犱犪狋犪犫犪狊犲'犐犖(

犛犈犔犈犆犜犆犖犃犕犈犉犚犗犕犆犗犝犚犛犈犛犠犎犈犚犈犆犗犝犚犛犈犛.犆犐犇=犆犎犗犐犆犈犛.犆犐犇)

(12)要求出选择 同 一 课 程 的 所 有 学 生 对,必 须 对 表CHOICES作 自 身 的 连 接 使 用

SQL语句来查询,需要两个不同名称来标志同一个表CHOICES。可以通过如下语句对

表起不同的别名来实现(见1_2_15.sql):

犛犈犔犈犆犜犡.犜犐犇,犢.犜犐犇犉犚犗犕犆犎犗犐犆犈犛犡,犆犎犗犐犆犈犛犢犠犎犈犚犈犡.犆犐犇=犢.犆犐犇犃犖犇犡.犖犗<犢.犖犗

对表起别名,SQL语句支持多种表达形式。通过下面的SQL语句也是可以得到相

同的结果(见1_2_16.sql):

犛犈犔犈犆犜犡.犜犐犇,犢.犜犐犇犉犚犗犕犆犎犗犐犆犈犛犃犛犡,犆犎犗犐犆犈犛犃犛犢犠犎犈犚犈犡.犆犐犇=犢.犆犐犇犃犖犇犡.犖犗<犢.犖犗

使用了条件X.NO<Y.NO是为了 保 证 元 组X和 元 组 Y是 不 同 的 元 组(因 为 在 表

CHOICES中,列NO具有唯一性),也就是确保每个学生对中,不存在学生自己与自己组

成的对。(13)查询至少被两位学生选修的课程编号,同样可以用表别名来解决这个问题。因

为要求是两位学生,所以仍需加上条件X.NO<Y.NO。SQL语句如下(见1_2_17.sql):

犛犈犔犈犆犜犡.犆犐犇犉犚犗犕犆犎犗犐犆犈犛犃犛犡,犆犎犗犐犆犈犛犃犛犢犠犎犈犚犈犡.犆犐犇=犢.犆犐犇犃犖犇犡.犖犗<犢.犖犗

(14)查询选修了编号850955252的学生所选的某个课程的学生编号。在这个查询中,首先要找出编号为80009026的学生选修的全部课程,再找出至少选

Page 25: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

18   数据库系统实验指导教程

修了其中某一个课程的学生编号。事实上,这道题仍然是对同一个表先做连接操作再选

择元组的类型。SQL语句如下(见1_2_18.sql):

犛犈犔犈犆犜犢.犛犐犇犉犚犗犕犆犎犗犐犆犈犛犃犛犡,犆犎犗犐犆犈犛犃犛犢犠犎犈犚犈犡.犆犐犇=犢.犆犐犇犃犖犇犡.犛犐犇='850955252'

在本题中,如果要求是“查询选修了编号850955252的学生所选的某个课程的其他学

生编号”时,也就 是 不 包 含 编 号850955252学 生 自 身 的 情 况 时,在 查 询 的 条 件 WHERE中,还需要加上条件X.NO<Y.NO。

(15)查询学生的基本信息及选修课程编号和成绩。在这个查询中,需要以表STUDENTS为主体列出每个学生的基本情况及其选课情

况,若某个学生没有选课,则只输出其基本情况信息,其选课信息为空值即可,这时就需要

使用外连接(OuterJoin)。使用的SQL语句如下(见1_2_19.sql):

犛犈犔犈犆犜犛犜犝犇犈犖犜犛.犛犐犇,犛犜犝犇犈犖犜犛.犛犖犃犕犈,犛犜犝犇犈犖犜犛.犌犚犃犇犈,犆犎犗犐犆犈犛.犆犐犇,犆犎犗犐犆犈犛.犛犆犗犚犈犉犚犗犕犛犜犝犇犈犖犜犛犑犗犐犖犆犎犗犐犆犈犛犗犖犛犜犝犇犈犖犜犛.犛犐犇=犆犎犗犐犆犈犛.犛犐犇

(16)查询学号850955252的学生的姓名和选修的课程名称及成绩。这个查询涉及三张表的查询:表STUDENTS、表COURSES和表CHOICES。可以

对三个表进行连接,得到所需信息。SQL语句如下(见1_2_20.sql):

犛犈犔犈犆犜犛犜犝犇犈犖犜犛.犛犖犃犕犈,犆犗犝犚犛犈犛.犆犖犃犕犈,犆犎犗犐犆犈犛.犛犆犗犚犈犉犚犗犕犛犜犝犇犈犖犜犛,犆犗犝犚犛犈犛,犆犎犗犐犆犈犛犠犎犈犚犈犛犜犝犇犈犖犜犛.犛犐犇=犆犎犗犐犆犈犛.犛犐犇犃犖犇犆犗犝犚犛犈犛.犆犐犇=犆犎犗犐犆犈犛.犆犐犇犃犖犇犛犜犝犇犈犖犜犛.犛犐犇='850955252'

说明:以上(10)~(16)都属于连接查询,包括笛卡儿连接、等值连接、自连接、外连接

和多表连接等情况。(17)查询与学号850955252的学生同年级的所有学生资料,首先考虑子查询中查询

编号850955252的学生的年级,在外层查询查询这些学生的资料,那么下面的SQL语句

是否符合要求呢(见1_2_21.sql)?

犛犈犔犈犆犜犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈(犛犈犔犈犆犜犌犚犃犇犈

犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈犛犐犇='850955252'

)=犌犚犃犇犈

将其写入到查询分析器中,执行后会发现上述语句产生了一个编译错误。这是什么

原因呢?原来,在SQL语句中规定子查询一定要跟在比较符之后,所以上面的语句应该

改成下面的形式,才能得到正确的结果(见1_2_22.sql)。

犛犈犔犈犆犜犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈犌犚犃犇犈=(

Page 26: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

19   第1章 SQL语言

犛犈犔犈犆犜犌犚犃犇犈犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈犛犐犇='850955252'

(18)查询所有有选课的学生信息,可以采用嵌套查询的方式来实现。在子查询中查

询所有有选课的学生的学号,构成一个集合。在外层查询中,查询学生表中的所有学号属

于这个集合的元组的信息。可以看出,这是一个不相关的子查询。可以在父查询的条件

中使用IN操作符,在其后接子查询。SQL语句如下(见1_2_23.sql):

犛犈犔犈犆犜犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈犛犐犇犐犖(

犛犈犔犈犆犜犛犐犇犉犚犗犕犆犎犗犐犆犈犛)

子查询的语句形式和普通查询大致是一样的。但是,子查询中没有ORDERBY子

句,而且外层SELECT语句的变量可以用在子查询中,而子查询的SELECT语句的变量

不能用于外层的查询中。(19)查询没有 学 生 选 的 课 程 的 编 号,可 以 采 用 与 上 题 相 类 似 的 方 法,不 过 是 使 用

NOTIN操作符来实现。SQL语句如下(见1_2_24.sql):

犛犈犔犈犆犜犆犖犃犕犈犉犚犗犕犆犗犝犚犛犈犛犠犎犈犚犈犆犐犇犖犗犜犐犖(

 犛犈犔犈犆犜犆犐犇犉犚犗犕犆犎犗犐犆犈犛)

(20)查询选修了课程名为C++的学生学号和姓名,考虑采用多重嵌套 子 查 询 来 实

现。由于所采用的都是不相关的子查询,所以采用嵌套结构逐次求解,将复杂的问题转化

为多个相对简单的问题来求解。在这道题中,将问题分成三个查询来实现。

① 最里层的子查询是在表COURSES中找出课程名称为Java的课程编号。

② 第二次子查询是在表CHOICES中找出选择了上个查询得到的课程号的学生编号。

③ 最外层查询是在表STUDENTS中取出编号为上个查询得到的学生编号的学生

的编号和姓名。由这三个查询构成的符合条件的SQL语句如下(见1_2_25.sql):

犛犈犔犈犆犜犛犐犇,犛犖犃犕犈犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈犛犐犇犐犖

Page 27: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

20   数据库系统实验指导教程

犛犈犔犈犆犜犛犐犇犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犆犐犇犐犖

犛犈犔犈犆犜犆犐犇犉犚犗犕犆犗犝犚犛犈犛犠犎犈犚犈犆犖犃犕犈='犆++'

))

(21)找出选修课程成绩最好的选课记录。查询成绩最差的情况,可以使用比较符和谓词的结合<ALL来实现。ALL后面跟着一

个查询成绩的子查询,就可以得到一个所有成绩的集合。满足关系不大于这个集合的任意

元素的成绩就一定是最小的,也即是本题所要求的,SQL语句如下(见1_2_26.sql):

犛犈犔犈犆犜犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犆犎犗犐犆犈犛.犛犆犗犚犈>=犃犔犔

犛犈犔犈犆犜犛犆犗犚犈犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犛犆犗犚犈犐犛犖犗犜犖犝犔犔)

思考:如果在子查询省略了 WHERE子句会得到什么结果?为什么?

(22)找出和课程UML或课程C++课时一样的课程名称,可以使用比较符和谓词的

结合=SOME来实现。SQL语句如下(见1_2_27.sql):

犛犈犔犈犆犜犆犖犃犕犈犉犚犗犕犆犗犝犚犛犈犛犠犎犈犚犈犎犗犝犚=犛犗犕犈(

犛犈犔犈犆犜犎犗犝犚犉犚犗犕犆犗犝犚犛犈犛犠犎犈犚犈犆犖犃犕犈='犝犕犔'犗犚犆犖犃犕犈='犆++'

这道题有多个等价形式。首先,将=SOME换成=ANY,得到的结果是一样的。这

是因为在SQL语句中认为SOME和ANY都是表示任意的一个值。其次,把=SOME换成谓词IN也是可行的。事实上,谓词=SOME和谓词IN的作

用是一样。需要注意的是,<>SOME跟NOTIN则是不同的。NOTIN实际是跟<>ALL等价。在使用的时候需要小心对待。

注意:只有在内层子 查 询 的 返 回 值 是 单 值 时,才 可 以 使 用 带 比 较 运 算 符 的 子 查 询。否则,会产生编译错误。

(23)查 询 所 有 选 修 编 号10001的 课 程 的 学 生 姓 名,可 以 理 解 为 查 找 在 表

Page 28: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

21   第1章 SQL语言

CHOICES中是否存在连接课程编号10001和 学 生 姓 名SNAME的 元 组。查 找 是 否 存

在符合某种条件的 元 组,可 以 使 用 谓 词EXISTS来 实 现。当EXISTS后 面 的 子 查 询 返

回空集合时,EXISTS判断的结果是假,而当后面的子 查 询 返 回 非 空 集 合 时,EXISTS判

断的结果是真,即满足条件。NOTEXISTS的 结 果 刚 好 相 反。SQL语 句 如 下(见1_2_

28.sql):

犛犈犔犈犆犜犛犖犃犕犈犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈犈犡犐犛犜犛(

犛犈犔犈犆犜犉犚犗犕犆犎犗犐犆犈犛犡犠犎犈犚犈犡.犆犐犇='10001'犃犖犇犡.犛犐犇=犛犜犝犇犈犖犜犛.犛犐犇)

本题的子查询属于相关子查询,因为内层的SELECT语句需要接收外层传递的数据

STUDENTS.SID。(24)查询选修了所有课程的学生姓名。这道题是查询具有全称量词FORALL(所有)的情况。根据在实验内容中所指出的

解题步骤,考虑对这个查询条件的形式进行变形。首先,考虑条件表达式1,表示存在的课程X,学生S没有选修,也即是在选课记录表

CHOICES中,没有学生S选修课程X的记录。条件表达式1的形式是:

犖犗犜犈犡犐犛犜犛(犛犈犔犈犆犜犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犛犐犇=犛犜犝犇犈犖犜犛.犛犐犇犃犖犇犆犐犇=犡.犆犐犇)

接着,考虑条件表达式2,表示不存在条件表达式1所表示的这种反例,即在选课记

录表CHOICES中不存在学生S没有选的课程,这就是本题所要求的条件。条件表达式

2如下:

犖犗犜犈犡犐犛犜犛(犛犈犔犈犆犜犉犚犗犕犆犎犗犐犆犈犛犃犛犡犠犎犈犚犈条件表达式1)

结合起来,就是本题要求的答案(见1_2_29.sql):

犛犈犔犈犆犜犛犖犃犕犈犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈犖犗犜犈犡犐犛犜犛  (犛犈犔犈犆犜

犉犚犗犕犆犎犗犐犆犈犛犃犛犡

Page 29: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

22   数据库系统实验指导教程

犠犎犈犚犈犖犗犜犈犡犐犛犜犛(犛犈犔犈犆犜犉犚犗犕犆犎犗犐犆犈犛犃犛犢犠犎犈犚犈犢.犛犐犇=犛犜犝犇犈犖犜犛.犛犐犇犃犖犇犢.犆犐犇=犡.犆犐犇)

  )

说明:以上(17)~(25)都属于嵌套查询,包括使用带谓词IN,带比较运算符,带谓词

SOME、ANY或ALL,带谓词EXISTS等四类嵌套查询方式。(25)使用集合查询方式查询选择课程C++或选择课程Java的学生的编号。根据条

件,可以采用集合运算符号UNION来实现或运算。SQL语句如下(见1_2_30.sql):

犛犈犔犈犆犜犜犐犇犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犆犎犗犐犆犈犛.犆犐犇=(

犛犈犔犈犆犜犆犗犝犚犛犈犛.犆犐犇犉犚犗犕犆犗犝犚犛犈犛犠犎犈犚犈犆犗犝犚犛犈犛.犆犖犃犕犈='犆++'

犝犖犐犗犖犛犈犔犈犆犜犜犐犇犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犆犎犗犐犆犈犛.犆犐犇=(

犛犈犔犈犆犜犆犗犝犚犛犈犛.犆犐犇犉犚犗犕犆犗犝犚犛犈犛犠犎犈犚犈犆犗犝犚犛犈犛.犆犖犃犕犈='犑犪狏犪'

如果不使用集合查询方式,而只是采用OR连接两个判断条件是否能够实现本题的

要求呢?如果采用下列语句(见1_2_31.sql):

犛犈犔犈犆犜犜犐犇犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犆犎犗犐犆犈犛.犆犐犇=(

犛犈犔犈犆犜犆犗犝犚犛犈犛.犆犐犇

 犉犚犗犕犆犗犝犚犛犈犛

 犠犎犈犚犈犆犗犝犚犛犈犛.犆犖犃犕犈='犆++'犗犚犆犗犝犚犛犈犛.犆犖犃犕犈='犑犪狏犪'

直接输入上述语句在 MicrosoftSQLServer的查询分析器中执行,会出现错误,它的

提示是:“子查询返回的值多于一个。当子查询跟随在 =、!=、<、<=、>、>= 之后,或子查询用作表达式时,这种情况是不允许的。”这也就是说,这组语句违反了在前面提到

Page 30: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

23   第1章 SQL语言

的使用比较运算符必须确保子查询返回是单值的要求。所以,必须将上面的SQL语句作

适当修改,将其改成下面语句就可以正确执行(见1_2_32.sql)。

犛犈犔犈犆犜犜犐犇犉犚犗犕犆犎犗犐犆犈犛,犆犗犝犚犛犈犛犠犎犈犚犈犆犎犗犐犆犈犛.犆犐犇=犆犗犝犚犛犈犛.犆犐犇犃犖犇(犆犗犝犚犛犈犛.犆犖犃犕犈='犆++'犗犚犆犗犝犚犛犈犛.犆犖犃犕犈='犑犪狏犪')

注意:在 WHERE语 句 中 的 运 算 符 AND的 结 合 的 优 先 级 高 于 OR,所 以 上 例 中

WHERE子句里面的括号是不可缺少的。比较一下上面两种方法,可以发现使用嵌套查询和等值连接查询在很多时候都可以

查询得到相同的结果,然而,实际当中可以发现使用嵌套查询的效率更高一些。(26)实现集合交运算,查询既选修课程C++又选修课程Java的学生的编号。在 MicrosoftSQLServer2000中不支持直接使用保留字INTERSECT进行交运算,

可以考虑使用AND逻辑与运算符来实现,SQL语句如下(见1_2_33.sql):

犛犈犔犈犆犜犡.犛犐犇犉犚犗犕犆犎犗犐犆犈犛犃犛犡,犆犎犗犐犆犈犛犃犛犢犠犎犈犚犈(犡.犆犐犇=(

犛犈犔犈犆犜犆犐犇犉犚犗犕犆犗犝犚犛犈犛犠犎犈犚犈犆犖犃犕犈='犆++'

)犃犖犇犢.犆犐犇=(

犛犈犔犈犆犜犆犐犇犉犚犗犕犆犗犝犚犛犈犛犠犎犈犚犈犆犖犃犕犈='犑犪狏犪'

))犃犖犇犡.犛犐犇=犢.犛犐犇

(27)实现集合减运算,查询选修课程C++而没有选修课程Java的学生的编号。在 MicrosoftSQLServer2000中不支持直接使用保留字EXCEPT进行交运算,可

以考虑使用NOT运算符来实现,SQL语句如下(见1_2_34.sql):

犛犈犔犈犆犜犡.犛犐犇犉犚犗犕犆犎犗犐犆犈犛犃犛犡,犆犎犗犐犆犈犛犃犛犢犠犎犈犚犈犡.犆犐犇=(

犛犈犔犈犆犜犆犐犇犉犚犗犕犆犗犝犚犛犈犛犠犎犈犚犈犆犖犃犕犈='犆++'

)犃犖犇犡.犛犐犇=犢.犛犐犇犃犖犇犖犗犜(犢.犆犐犇=(

犛犈犔犈犆犜犆犐犇犉犚犗犕犆犗犝犚犛犈犛犠犎犈犚犈犆犖犃犕犈='犑犪狏犪'

))

Page 31: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

24   数据库系统实验指导教程

说明:以上(25)~(27)考虑使用集合运算的情况。如果SQLServer支持直接使用

保留字UNION、INTERSECT和EXCEPT进行集合 运 算 则 直 接 用 之 就 可 以。否 则,可

以考虑使用逻辑运算符如AND和NOT来实现。提示:事实上,在具体的实验中,可以发现SQL的查询语句很多都具有等价的形式。

所以,上述各题的解题的方法并非都是唯一可行的方法,可能还有其他多种解题方法。读

者不妨尝试一下用其他方法来完成上述各题。

5. 习题

(1)查询全部课程的详细记录。(2)查询所有有选课的学生的编号。(3)查询课时<88(小时)的课程的编号。(4)请找出总分超过400分的学生。(5)查询课程的总数。(6)查询所有课程和选修该课程的学生总数。(7)查询选修成绩合格的课程超过两门的学生编号。(8)统计各个学生的选修课程数目和平均成绩。(9)查询选修Java的所有学生的编号及姓名。(10)分别使用等值连接和谓词IN两种方式查询姓名为sssht的学生所选的课程的

编号和成绩。(11)查询课时比课程C++多的课程的名称。(12)查询选修C++课程的成绩比姓名为znkoo学生高的所有学生的编号和姓名。(13)找出和学生883794999或学生850955252的年级一样的学生的姓名。(14)查询没有选修Java的学生名称。(15)找出课时最少的课程的详细信息。(16)查询工资最高的教师的编号和开设的课程号。(17)找出选修课程ERP成绩最高的学生编号。(18)查询没有学生选的课程的名称。(19)找出讲授课程UML的教师讲授的所有课程名称。(20)查询选修了编号200102901的教师开设的所有课程的学生编号。(21)查询选修课程database的学生集合与选修课程UML的学生集合的并集。(22)实现集合交运算,查询既选修课程database又选修课程UML的学生的编号。(23)实现集合减运算,查询选修课程database而没有选修课程UML的学生的编号。

实验1.3 数据更新

1. 实验目的

  熟悉数据库的数据更新操作,能够使用SQL语句对数据库进行数据的插入、更新、删

Page 32: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

25   第1章 SQL语言

除操作。

2. 原理解析

(1)使用SQL语句对数据库进行数据插入操作,一般使用INSERT语句,它的一般

格式如下:

犐犖犛犈犚犜犐犖犜犗<基表名>[<列名>[,<列名>]…]

犞犃犔犝犈犛(<常量>[<常量>]…)|<子查询>

该语句的作用是执行一个插入操作,可以将 VALUES所给出的值插入INTO所指

定的表中或将子查询的结果插入到数据库中。在指定列名的时候,可以指定全部列或其中的几个列。当指定VALUES值的时候,

列名和插入的元组的VALUES后面所跟的数值必须一一对应。当只指定基本表中的某

几列而不是所有列时,必须保证被省略的这些列的取值是允许为空值,否则会出现错误。如果省略的列的取值允许为空值,则指定的元组(没有赋值的列的值被置为NULL值)可

以插入到数据库中。当插入的是子查询的结果的时候,必须保证子查询得到的数值类型与将插入的基本

表中对应列的数值类型是一致的。(2)进行数据的插入操作还可以用SELECTINTO的语句,一般格式是:

犛犈犔犈犆犜[犃犔犔|犇犐犛犜犐犖犆犜[犗犖(<目标列表达式> [,…])]]

|表达式 [犃犛<列名>][,…]

犐犖犜犗[犜犈犕犘犗犚犃犚犢|犜犈犕犘][犜犃犅犔犈]<表名>[犉犚犗犕<表名或视图名> [别名][,<表名或视图名> [别名]]…]][犠犎犈犚犈条件表达式][犌犚犗犝犘犅犢<列名1>[,<列名1,>]…[犎犃犞犐犖犌<条件表达式>]]

这个语句的作用是从一个查询的计算结果中创建一个新表。数 据 并 不 返 回 给 客 户

端,这一点和普通的SELECT不同,新表的字段具有和SELECT的输出字段相关联(相

同)的名字和数据类型。(3)DBMS在执行插入语句时会检查所插元组是否破坏表上已定义的完整性规则:

① 实体完整性。

② 参照完整性。

③ 用户定义的完整性。

对于有NOTNULL约束的属性列是否提供了非空值。

对于有UNIQUE约束的属性列是否提供了非重复值。

对于有值域约束的属性列所提供的属性值是否在值域范围内。(4)对数据的修改,可以使用UPDATE语句,一般形式是:

犝犘犇犃犜犈<基表名>犛犈犜<列名>=表达式[,<列名>=表达式]…

Page 33: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

26   数据库系统实验指导教程

犠犎犈犚犈<逻辑条件>

该语句的作用是修改指定的基本表中满足 WHERE子句的条件的元组,并把这些元

组按照SET子句中的表达式修改相应列上的值。UPDATE语句的一个语句可以修改一

个记录,或同时修改多个记录。(5)使用UPDATE语句可以修改指定表中满足 WHERE子句条件的元组,有三种

修改的方式:

修改某一个元组的值。

修改多个元组的值。

带子查询的修改语句。

同样地,DBMS在执行修改语句时会检查修改操作是否破坏表上已定义的完整性规

则,除上面插入操作需要满足的完整性,更新操作要满足外,更新操作还要满足主码不允

许修改的约束。(6)数据删除的SQL语句:

犇犈犔犈犜犈

犉犚犗犕<基表名>犠犎犈犚犈<逻辑条件>

该语句表示“从 指 定 的 表 中 删 除 满 足 WHERE子 句 条 件 的 所 有 元 组”,当 WHERE子句省略时,则表示删除表中所有元组。对于省略了 WHERE子句的删除语句,用户使

用时必须慎重对待。DELETE在删除数据是以元组为单位进行删除的。当需要对某个

元组的某个属性值删除时,只通过能使用UPDATE语句去更新元组实现,或是通过将这

个元组 用 DELETE 删 除 再 插 入 一 个 新 的 符 合 要 求 的 元 组 的 方 法 来 实 现。直 接 用

DELETE语句来删除元组的某个属性值是没有办法实现的。(7)使用DELETE语句删除数据也有三种方式:

删除某一个元组的值。

删除多个元组的值。

带子查询的删除语句。

DBMS在执行删除语句时会检查所插元组是否破坏表上已定义的参照完整性约束。

3. 实验内容

在本实验中,主要的内容是如何用SQL语句对数据进行更新。

使用INSERTINTO语句插入数据,包括插入一个元组或将子查询的结果插入到

数据库中两种方式。

使用SELECTINTO语句,产生一个新表并插入数据。

使用UPDATE语句可以修改指定表中满足 WHERE子句条件的元组,有三种修

改的方式:修改某一个元组的值;修改多个元组的值;带子查询地修改语句。

使用DELETE语句删除数据:删除某一个元组的值;删除多个元组的值;带子

查询地删除语句。

Page 34: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

27   第1章 SQL语言

4. 实验步骤

1)要求

在数据库School上按下列要求进行数据更新。(1)使 用 SQL 语 句 向 STUDENTS 表 中 插 入 元 组 (编 号:700045678。名 字:

LiMing。EMAIL:LX@cdemg.com。年级:1992)。(2)对每个课程,求学生的选课人数和学生的平均成绩,并把结果存入数据库。使用

SELECTINTO和INSERTINTO两种方法实现。(3)在STUDENTS表中使用SQL语句将姓名为LiMing的学生的年级改为2002。(4)在TEACHERS表中使用SQL语句将所有教师的工资加500元。(5)将姓名为zapyv的学生的课程C的成绩加上5分。(6)在STUDENTS表中使用SQL语句删除姓名为LiMing的学生信息。(7)删除所有选修课程Java的选课记录。(8)对COURSES表 做 删 去 时 间<48的 元 组 的 操 作,讨 论 该 删 除 操 作 所 受 到 的

约束。

2)分析与解答

(1)使 用 SQL 语 句 向 STUDENTS 表 中 插 入 元 组 (编 号:700045678。名 字:

LiMing。EMAIL:LX@cdemg.com。年级:1992)向数据库插入一个指定的元组,可以使用INSERT语句,格式如下(见1_3_1.sql):

犐犖犛犈犚犜犐犖犜犗犛犜犝犇犈犖犜犛犞犃犔犝犈犛('700045678','犔犻犕犻狀犵','犔犡@犮犱犲犿犵.犮狅犿',1992)

在本题中,表名STUDENTS后面的列名都被省略了,在VALUES里面已经给定了

所有的列的取值(各个列的顺序应该与表的定义相一致)的时候,这种做法是可行的。当

然,在STUDENTS后面写出对一般对应的各个列的列名,也可以得到相同结果。当插入

的元组的各个属性都有赋值时,在SQL语句中可以不用写出列名。而在其他情况下,也

就是只指定部分列的值的时候,则需要按照相应的顺序把有指定值的属性名都 写 下 来。同时,还必须满足一个条件就是没有指定值的列在前面的定义中是允许为空值的。

(2)对每个课程,求学生的选课人数和学生的平均成绩,并把结果存入数据库。在数

据库School中并不存在这样的表,如果使用INSERTINTO语句,可以通过先建表,再插

入数据。方法如下:第一步,建表(见1_3_2.sql)。

犆犚犈犃犜犈犜犃犅犔犈犆犎犗犐犆犈犛犚犈犛犝犔犜(

犆犐犇犆犎犃犚(10),

犛犜犝犇犈犖犜犛犛犕犃犔犔犐犖犜,

犃犞犌犛犆犗犚犈犛犕犃犔犔犐犖犜)

Page 35: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

28   数据库系统实验指导教程

第二步,插入数据(见1_3_3.sql)。

犐犖犛犈犚犜犐犖犜犗犆犎犗犐犆犈犛犚犈犛犝犔犜犛犈犔犈犆犜犆犐犇,犆犗犝犖犜(犛犐犇),犃犞犌(犛犆犗犚犈)

犉犚犗犕犆犎犗犐犆犈犛犌犚犗犝犘犅犢犆犐犇

在第二步的实验中,可以将子查询的结果的数据直接插入到表CHOICESRESULT中。在子查询中可以返回多个元组,在这里仅用一个语句就可以实现将这多个元组插入

到数据库中。实际上,SQL还提供了更方便的方法来实现上面例子的功能来插入数据。可以使用

SELECTINTO语句来实现,具体语句如下(见1_3_4.sql):

犛犈犔犈犆犜犆犐犇犃犛犆犐犇,犆犗犝犖犜(犛犐犇)犃犛犛犜犝犇犈犖犜犛,犃犞犌(犛犆犗犚犈)犃犛犃犞犌犛犆犗犚犈犐犖犜犗犆犎犗犐犆犈犛犚犈犛犝犔犜犉犚犗犕犆犎犗犐犆犈犛犌犚犗犝犘犅犢犆犐犇

SELECTINTO与INSERTINTO的最主要区别在于,SELECTINTO可以同时建

立一个新表,不需要像INSERTINTO那样要先定义了表才能够插入数据。(3)在STUDENTS表中使用SQL语句将姓名为LiMing的学生的年级改为2002。在这个修改操作中,需要修改指定的元组,可以使用 WHERE子句来实现,符合要求

的SQL语句如下(见1_3_5.sql):

犝犘犇犃犜犈犛犜犝犇犈犖犜犛犛犈犜犌犚犃犇犈='2002'

犠犎犈犚犈犛犖犃犕犈='犔犻犕犻狀犵'

刷新数据库后,查看该元组,可以发现其属性GRADE的值已经更新为2002了。(4)在TEACHERS表中使用SQL语句将所有教师的工资加500元。对所有的教师的工资进行修改,WHERE子句可以省 略。在 此 操 作 中,并 不 是 该 数

据改为指定数值,而是做一个加法,使得所有的工资都增加500。因而可以将SET子句

写成对属性SALARY进行运算的形式,符合要求的SQL语句如下(见1_3_6.sql):

犝犘犇犃犜犈犜犈犃犆犎犈犚犛犛犈犜犛犃犔犃犚犢=犛犃犔犃犚犢+500

在该操作中,数据库中TEACHERS表的所有元组(共15000个)都得到了更新,列

名为SALARY的这一列所有元素的数值都比更新前增加了500。(5)将姓名为zapyv的学生的课程C的成绩加上5分。在这个操作里面,需要修改的元组没法直接使用一个 WHERE子句来实现。这时,

可 以 通 过 使 用 嵌 套 语 句 来 实 现 对 元 组 的 筛 选。符 合 条 件 的 SQL 语 句 如

下(见1_3_7.sql):

犝犘犇犃犜犈犆犎犗犐犆犈犛犛犈犜犛犆犗犚犈=犛犆犗犚犈+5

Page 36: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

29   第1章 SQL语言

犠犎犈犚犈犆犎犗犐犆犈犛.犖犗=(

犛犈犔犈犆犜犆犎犗犐犆犈犛.犖犗犉犚犗犕犆犎犗犐犆犈犛,犆犗犝犚犛犈犛,犛犜犝犇犈犖犜犛犠犎犈犚犈犆犗犝犚犛犈犛.犆犐犇=犆犎犗犐犆犈犛.犆犐犇犃犖犇犛犜犝犇犈犖犜犛.犛犐犇=犆犎犗犐犆犈犛.犛犐犇犃犖犇犆犗犝犚犛犈犛.犆犖犃犕犈='犆'

犃犖犇犛犜犝犇犈犖犜犛.犛犖犃犕犈='狕犪狆狔狏'

通过查询 数 据 库,可 以 发 现 在 CHOICES表 中 对 应 的 元 组(NO=541282386)的

SCORE值已经发生了相应的更新。(6)在STUDENTS表中使用SQL语句删除姓名为LiMing的学生信息。使用DELETE语句删除 元 组,必 须 在 WHERE子 句 中 明 确 指 出 删 除 的 对 象,如 果

WHERE语句省略,则是整张表的数据都删除。所以,使用DELETE语句的时候要比较

小心,按要求写好 WHERE子句。符合该操作要求的SQL语句如下(见1_3_8.sql):

犇犈犔犈犜犈犛犜犝犇犈犖犜犛犠犎犈犚犈犛犖犃犕犈='犔犻犕犻狀犵'

(7)删除所有选修课程Java的选课记录,SQL语句如下(见1_3_9.sql):

犇犈犔犈犜犈犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈'犑犪狏犪'=(

犛犈犔犈犆犜犆犖犃犕犈犉犚犗犕犆犗犝犚犛犈犛犠犎犈犚犈犆犗犝犚犛犈犛.犆犐犇=犆犎犗犐犆犈犛.犆犐犇)

删除语句在执行过程中实际上就是按照 WHERE子句条件,每找到一个元组,将其

删除。因而,也可以在 WHERE子句中实行嵌套。(8)在进行DELETE操作时,还需要注意到是否满足表定义中的约束关系。例如在

COURSES表中删去时间<48的元组的操作就会出错,SQL语句如下(见1_3_10.sql):

犇犈犔犈犜犈犆犗犝犚犛犈犛犠犎犈犚犈犎犗犝犚<48

运行的时候则会出现下面的错误提示:“DELETE语 句 与 COLUMNREFERENCE 约 束 'FK_CHOICES_COURSES'冲

突。该冲突发生于数据库 'School',表 'CHOICES',column 'cid'。语句已终止。”这是由于COURSES表中的CID属性,在CHOICES表是作为外键存在,存在约束

FK_CHOICES_COURSES(这是在创建 表 的 时 候 定 义 的,属 于 用 户 自 定 义 约 束)。该 约

束限制了对表COURSES的删除操作。所以,在对数据进行删除时,要注意数据库中的约束关系。如果不能满足已有的约束

关系,则删除操作无法完成。注意:对数据的操作可以通过查询分析器使用上述的SQL语句进行,也可以直接在

数据库中进行。步骤如下:

Page 37: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

30   数据库系统实验指导教程

(1)打开企业管理器,双击选定的数据库School。(2)选定需要更新的基本表,单击右键,选择快捷菜单中的“打开表”,再单击“返回所

有选项”按钮,直接进行编辑。

5. 习题

(1)向STUDENTS表插入编号是800022222且姓名是 WangLan的元组。(2)向TEACHERS表插入元组(200001000,LXL,s4zrck@pew.net,3024)。(3)将TEACHERS表中编号为200010493的老师工资改为4000。(4)将TEACHERS表中所有工资小于2500的老师工资改为2500。(5)将由编号200016731老师讲授的课程全部改成由姓名rnupx的老师讲授。(6)更新编号800071780的学生年级为2001。(7)删除没有学生选修的课程。(8)删除年级高于1998的学生信息。(9)删除没有选修课程的学生信息。(10)删除成绩不及格的选课记录。

实验1.4 视图

1. 实验目的

  熟悉SQL支持的有关视图的操作,能够熟练使用SQL语句来创建需要的视图,对视

图进行查询和取消视图。

2. 原理解析

视图是虚表,是从一个或几个基本表(或视图)导出的表,在数据库中只存放视图的定

义,不会出现数据冗余。当基表中的数据发生变化,从视图中查询出的数据也随之改变。视图只是基本表数据的一个窗。

常见的视图形式包括:

行列子集视图,这种视图是由单个基本表通过选择和投影操作导出的,并且该视

图的属性集包含基本表的一个候选键。

WITHCHECKOPTION的视图,在视图的更新操作时进行合法性检查。

基于多个基表的视图。

基于视图的视图,即由视图导出的视图。

带表达式的视图。

分组视图。基于视图的操作,包括查询、删除、受限更新和定义基于该视图的新视图。(1)SQL语言创建视图的语句格式:

犆犚犈犃犜犈犞犐犈犠<视图名>(<列名>[,<列名>]…])

Page 38: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

31   第1章 SQL语言

犃犛<映像语句>[犠犐犜犎犆犎犈犆犓犗犘犜犐犗犖]

其中,<映像语句>可以是任意复杂的SELECT语句,也可以是带运算符号的表达

式,但其中不包含有ORDERBY子句和DISTINCT子句。WITHCHECKOPTION表

示用视图进行更新、插入或删除操作时,要保证更新的元组满足视图定义中的谓词条件,

即映像语句中的条件表达式。执行CREATEVIEW 语句的时候,没有数据被检索或存储。但是,视图的定义作为

数据 库 中 一 个 独 立 的 对 象 存 放 在 系 统 目 录 中,以 备 其 他 的 查 询 或 UPDATE语 句 中 的

FROM子句中以这一个视图的名称对其进行检索。定义视 图 有 两 种 方 式:属 性 名 称 全 部 省 略 或 全 部 指 定。当 视 图 是 由 子 查 询 中

SELECT目标列中的诸字段组成时,属性可以省略。而在以下情况下必须明确指定视图

的所有列名:

某个目标列是集函数或列表达式。

目标列为。

多表连接时选出了几个同名列作为视图的字段。

需要在视图中为某个列启用新的更合适的名字。(2)使用取消视图语句将视图删除,形式如下:

犇犚犗犘犞犐犈犠<视图名>

需注意的是,视图被取消之后,由该视图导出的其他视图定义虽然仍然保留在数据字

典中,但是它们都已经失效,用户使用时就会出错,因此需要进一步DROP语句将它们一

一显式地删除。(3)对视图的操作主要是利用视图来进行数据查询。从用户的角度,对视图进行查

询与对基本表的查询是一样的。(4)在一般 情 况 下,视 图 的 定 义 一 般 不 能 更 改,不 能 像 基 本 表 一 样 通 过 直 接 使 用

ALTER语句来更改表的定义。因为视图只是一种映射而成的虚表,一经定义,它的内部

结构就确定下来,不会改变。当需要修改视图的结构的时候,通常的做法是先将原来的视

图删除,再重新定义新的符合要求的视图。(5)对视图做插入及更新 数 据 操 作 具 备 一 定 难 度。因 为 视 图 仅 是 一 张 虚 拟 的 表,

而非实际存在数据库 中,对 它 进 行 更 新 可 能 导 致 数 据 库 出 现 数 据 不 一 致 现 象。因 而,对视图作更新操作一般是不可行的。只有当 视 图 是 行 列 子 集 视 图 时,才 可 以 执 行 更 新

操作。实际中的系统一般都允许对行列子集视图进行更新,而对其他类型视图的更新,不同

系统有不同限制。

DB2对视图更新的限制如下:

① 若视图是由两个以上基本表导出的,则此视图不允许更新。

② 若 视 图 的 字 段 来 自 字 段 表 达 式 或 常 数,则 不 允 许 对 此 视 图 执 行INSERT 和

UPDATE操作,但允许执行DELETE操作。

Page 39: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

32   数据库系统实验指导教程

③ 若视图的字段来自集函数,则此视图不允许更新。

④ 若视图定义中含有GROUPBY子句,则此视图不允许更新。

⑤ 若视图定义中含有DISTINCT短语,则此视图不允许更新。

⑥ 若视图定义中有嵌套查询,并且内层查询的FROM 子句中涉及的表也是导出该

视图的基本表,则此视图不允许更新。

⑦ 一个不允许更新的视图上定义的视图也不允许更新。(6)物化视图,是一种比较特殊的视图。在某些数据库系统允许存储视图关系。当

执行CREATEINDEX语句时,视图SELECT的结果集将永久存储在数据库中,这样形

成的视图称为物化视图。物化视图不像一般视图只保存定义,而是永久存储数据在数据

库中,同时,对基本数据的修改将自动反映在视图中。在Oracle数据库中,物化视图是存储一 个 查 询 结 果 的 数 据 库 对 象。它 可 以 用 来 生

成基于数据表求和的汇总表,也可以存储基 于 远 程 表 的 本 地 副 本(只 读),也 称 为 快 照。如果用户想修 改 本 地 副 本,必 须 用 高 级 复 制 的 功 能。用 户 可 以 从 物 化 视 图 中 抽 取 数

据。对于数据仓库,创建 的 物 化 视 图 通 常 情 况 下 是 聚 合 视 图、单 一 表 聚 合 视 图 和 连 接

视图。物化视图的优点在于SQL语句此后若引用该视图,响应时间将会显著缩短。缺点则

是存储代价以及更新开销大。因而,物化视图适用于频繁使用某个视图的应用或基于视

图的查询需要快速响应的应用。

3. 实验内容

(1)定义常见的视图形式,包括:

行列子集视图。

WITHCHECKOPTION的视图。

基于多个基表的视图。

基于视图的视图。

带表达式的视图。

分组视图。(2)通过实验考察 WITHCHECKOPTION这一语句在视图定义后产生的影响,包

括对修改操作、删除操作、插入操作的影响。(3)讨论视图的数据更新情况,对子行列视图进行数据更新。(4)使用DROP语句删除一个视图,由该视图导出的其他视图定义仍在数据字典中,

但已不能使用,必须显式删除。同样的原因,删除基表时,由该基表导出的所有视图定义

都必须显式删除。

4. 实验步骤

1)要求

(1)创建一个行列子集视图,给出选课成绩合格的学生的编号,所选课程号和该课程

成绩。

Page 40: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

33   第1章 SQL语言

(2)创建基于多个基表的视图,这个视图由学生姓名和他所选修的课程名及讲授该

课程的教师姓名构成。(3)创建带表达式的视图,由学生姓名及所选课程名和所有课程成绩都比原来多5

分这几个属性组成。(4)创建分组视图,将学生的学号及他的平均成绩定义为一个视图。(5)创建一个基于视图的视图,基于(1)中建立的视图,定义一个包括学生编号、学生

所选课程数目和平均成绩的视图。(6)查询所有选修课程softwareengineering的学生姓名。(7)插入元组(600000000,823069829,10010,59)到视图CS中。若是在视图的定义

中存在 WITHCHECKOPTION子句对插入操作有什么影响?

(8)将视图CS(包含定义 WITHCHECKOPTION)中,所有课程编号为10010的课

程的成绩都减去5分。这个操作数据库是否会正确执行,为什么?如果加 上5分(原 来

95分以上的不变)呢?

(9)在视图CS(包含定义 WITHCHECKOPTION)删除编号804529880学生的记

录,会产生什么结果?

(10)取消视图SCT和视图CS。

2)分析和解答

(1)定义一个行列子集视图,给出选课成绩合格的学生的编号,所选课程号和该课程

成绩。在这个视图中,仅是对基本表CHOICES选出符合条件的元组(成绩合格),并只显示

出其中的学生编号,课程号和成绩三个属性。SQL语句如下(见1_4_1.sql):

犆犚犈犃犜犈犞犐犈犠犆犛犃犛犛犈犔犈犆犜犖犗,犛犐犇,犆犐犇,犛犆犗犚犈犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犛犆犗犚犈>=60

因为在这道题中,视图CS是由子查询中SELECT目标列中的诸字段组成,所以可以

将视图的各个列名省略。视图CS具有的这四个属性的数据类型与在表CHOICES中的

这四个属性的数据类型相同。(2)定义学生姓名和他所选修的课程名及讲授该课程的教师姓名构成的视图。在数据库School中,教师的姓名存在于基本表TEACHERS中,而课程名称存在于

COURSES中,学生姓名存在于STUDENTS表中,三者通过选课CHOICES表发生间接

联系,因而需要建立一个基于这四个基表的视图,将视图名定为SCT,可以使用下面SQL语句来实现(见1_4_2.sql):

犆犚犈犃犜犈犞犐犈犠犛犆犜(犛犖犃犕犈,犆犖犃犕犈,犜犖犃犕犈)

犃犛犛犈犔犈犆犜犛犜犝犇犈犖犜犛.犛犖犃犕犈,犆犗犝犚犛犈犛.犆犖犃犕犈,犜犈犃犆犎犈犚犛.犜犖犃犕犈犉犚犗犕犆犎犗犐犆犈犛,犛犜犝犇犈犖犜犛,犆犗犝犚犛犈犛,犜犈犃犆犎犈犚犛犠犎犈犚犈犆犎犗犐犆犈犛.犜犐犇=犜犈犃犆犎犈犚犛.犜犐犇犃犖犇犆犎犗犐犆犈犛.犆犐犇=犆犗犝犚犛犈犛.犆犐犇

Page 41: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

34   数据库系统实验指导教程

犃犖犇犆犎犗犐犆犈犛.犛犐犇=犛犜犝犇犈犖犜犛.犛犐犇

(3)定义由学生姓名及所选课程名和所有课程成绩都多5分的视图。在本题中,需要从STUDENTS表、COURSES表和CHOICES表三个表中选取合适

的列名来作为视 图 的 列 名。其 中,成 绩 比 原 来 多5分,可 以 直 接 使 用 下 列 表 达 式 得 到

(见1_4_3.sql)。

犆犚犈犃犜犈犞犐犈犠犛犆犆(犛犖犃犕犈,犆犖犃犕犈,犛犆犗犚犈)

犃犛犛犈犔犈犆犜犛犜犝犇犈犖犜犛.犛犖犃犕犈,犆犗犝犚犛犈犛.犆犖犃犕犈,犆犎犗犐犆犈犛.犛犆犗犚犈+5犉犚犗犕犆犎犗犐犆犈犛,犛犜犝犇犈犖犜犛,犆犗犝犚犛犈犛犠犎犈犚犈犆犎犗犐犆犈犛.犆犐犇=犆犗犝犚犛犈犛.犆犐犇犃犖犇犆犎犗犐犆犈犛.犛犐犇=犛犜犝犇犈犖犜犛.犛犐犇

在本视图中,存在着由表达式表示的属性,因而它是不允许进行数据更新的。(4)将学生的学号及他 的 平 均 成 绩 定 义 为 一 个 视 图,需 要 将 选 课 记 录 按 学 生 分 组。

SQL语句如下(见1_4_4.sql):

犆犚犈犃犜犈犞犐犈犠犛_犌(犛犐犇,犛犃犞犌)

犃犛犛犈犔犈犆犜犛犐犇,犃犞犌(犛犆犗犚犈)

犉犚犗犕犆犎犗犐犆犈犛犌犚犗犝犘犅犢犛犐犇

由于属性SAVG是由分组统计得到的,这样的视图是不允许更新的。(5)创建一个基于视图的视图,基于(1)中建立的视图,定义一个包括学生编号,学生

所选课程数目和平均成绩的视图。SQL语句如下(见1_4_5.sql):

犆犚犈犃犜犈犞犐犈犠犛_犆_犛(犛犐犇,犆犆犗犝犖犜,犛犃犞犌)

犃犛犛犈犔犈犆犜犛犐犇,犆犗犝犖犜(犆犛.犆犐犇),犃犞犌(犛犆犗犚犈)

犉犚犗犕犆犛犌犚犗犝犘犅犢犆犛.犛犐犇

(6)查询所有选修课程softwareengineering的学生姓名。对视图的查询操作与对基本表的查询操作使用的SQL语句格式是相同的,在这里仅

以此一例进行说明。一般情况下,对视图的查询操作都是对单个视图操作。因为视图本

身已经是由单表或多表映射而成的虚表,一般不再将视图与其他表做连接查询。符合要

求的SQL语句是(见1_4_6.sql):

犛犈犔犈犆犜犛犖犃犕犈犉犚犗犕犛犆犜犠犎犈犚犈犆犖犃犕犈='狊狅犳狋狑犪狉犲犲狀犵犻狀犲犲狉犻狀犵'

(7)插入元组(600000000,823069829,10010,59)到视图CS中。若是在视图的定义

中存在 WITHCHECKOPTION子句对插入操作有什么影响?

首先,该视图可以插入数据,因为该视图仅是对应的唯一的基本表的一个行列子集,

Page 42: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

35   第1章 SQL语言

它的每个属性列在数据库中都有唯一的基本表的列与它对应。同时,在该视图中被省略

的那些列在原来基本表中是可以为空的,这时才允许插入。如果这些在视图没有出现而

在原来基表存在的列是不允许为空的话,插入操作是不允许的。例如,在本实验的前面部

分建立的除了视图CS以外的所有视图都是不允许插入数据的。尝试向视图插入数据,

SQL语句如下(见1_4_7.sql):

犐犖犛犈犚犜犐犖犜犗犆犛犞犃犔犝犈犛('600000000','823069829','10010',59)

执行 之 后,查 询 数 据 库 可 以 看 到,在 基 本 表 CHOICES中 也 相 应 地 插 入 该 元 组

(600000000,823069829,NULL,10010,59)。再考虑另外一种情况,当在创建视图CS时,加上了 WITHCHECKOPTION子句

的时候,也就是使用如下语句创建视图CS(见1_4_8.sql):

犆犚犈犃犜犈犞犐犈犠犆犛(犖犗,犛犐犇,犆犐犇,犛犆犗犚犈)

犃犛犛犈犔犈犆犜犆犎犗犐犆犈犛.犖犗,犆犎犗犐犆犈犛.犛犐犇,犆犎犗犐犆犈犛.犆犐犇,犆犎犗犐犆犈犛.犛犆犗犚犈

犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犆犎犗犐犆犈犛.犛犆犗犚犈>=60犠犐犜犎犆犎犈犆犓犗犘犜犐犗犖

在这个时候,仍然使用1_4_7.sql中语句来插入元组,则会提示出现错误:“服务器:消息550,级别16,状态1,行1试图进行的插入或更新已失败,原因是目标视图或者目标视图所跨越的某一视图指

定了 WITH CHECK OPTION,而 该 操 作 的 一 个 或 多 个 结 果 行 又 不 符 合 CHECKOPTION约束的条件。语句已终止。”

从提示信息里面,可以看出,WITHCHECKOPTION这个子句使得所有的对视图

的插入或更新 操 作 都 必 须 满 足 定 义 视 图 时 指 明 的 条 件,在 本 题 中 是SCORE>=60。

1_4_7.sql中语句要插入的元组并不满足这个条件:SCORE=59<60。所以在本题中插

入这个元组是不成功的。实际上,任何对数据库中更新元组,使得SCORE<60的操作也

都不会成功,原因同上。(8)将视图CS(包含定义 WITHCHECKOPTION)中,所有课程编号为10010的课

程的成绩都减去5分。这个操作数据库是否会正确执行,为什么?如果加 上5分(原 来

95分以上的不变)呢?

对视图进行数据 更 新 与 对 基 本 表 进 行 数 据 更 新,在SQL语 句 的 形 式 上 是 一 样 的。

SQL语句如下(见1_4_9.sql):

犝犘犇犃犜犈犆犛犛犈犜犛犆犗犚犈=犛犆犗犚犈-5犠犎犈犚犈犆犐犇='10010'

执行之后,会发现数据库仍然拒绝这个操作,提示信息如上题。这是由于数据库中存

Page 43: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

36   数据库系统实验指导教程

在一些元组的SCORE值较低,当做运算SCORE=SCORE-5的时候,结果会<60,这就

不满足子句 WITHCHECKOPTION的条件,因而操作不能实现。

那么,如果成绩都加上5,能否实现呢?仍然使用如下SQL语句(见1_4_10.sql):

犝犘犇犃犜犈犆犛

犛犈犜犛犆犗犚犈=犛犆犗犚犈+5

犠犎犈犚犈犆犐犇='10010'犃犖犇犛犆犗犚犈<95

实 验 发 现 这 个 结 果 是 可 以 顺 利 执 行 的。这 个 操 作 能 够 满 足 WITH CHECKOPTION的条件,有4171个元组得到修改。这时候如果查看基本表CHOICES,可以发

现对应的记录都得到了更新。(9)在视图CS(包含定义 WITHCHECKOPTION)删除编号804529880学生的记

录,会产生什么结果?SQL语句如下(见1_4_11.sql):

犇犈犔犈犜犈犆犛

犠犎犈犚犈犛犐犇='804529880'

同样地,在进行数据删除时,DBS仍然会检查SCORE>60的这个条件是否满足。如

果不满足,数据就不会被删除。在这道题中,删除编号804529880学生的记录,由于条件

满足,删除操作顺利执行。

注意:在(7)~(9)中,探讨了SQL的 WITHCHECKOPTION子句在视图定义后

所起的作用。它会使得DBS在每次对视图的插入、更新和删除操作时,都先判断是否满

足视图定义中的条件,如果不满足则不执行操作。(10)取消视图SCT和视图CS。

取消视图的SQL语句和取消基本表的SQL语句形式是一样的,仅需要一个DROP语句就可以将其删除。SQL语句如下(见1_4_12.sql):

犇犚犗犘犞犐犈犠犛犆犜

犇犚犗犘犞犐犈犠犆犛

5. 习题

(1)定义选课信息和课程名称的视图VIEWC。(2)定义学生姓名与选课信息的视图VIEWS。(3)定义年级低于1998的学生的视图S1(SID,SNAME,GRADE)。(4)查询学生为uxjof的学生的选课信息。(5)查询选修课程UML的学生的编号和成绩。(6)向视图S1插入记录(60000001,Lily,2001)。(7)定义 包 括 更 新 和 插 入 约 束 的 视 图S1,尝 试 向 视 图 插 入 记 录(60000001,Lily,

1997),删除所有年级为1999的学生记录,讨论更新和插入约束带来的影响。(8)在将视图VIEWS中将姓名为uxjof的学生的选课成绩都加上5分。(9)取消以上建立的所有视图。

Page 44: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

37   第1章 SQL语言

实验1.5 数据控制

1. 实验目的

  熟悉SQL的数据控制功能,能够使用SQL语句来向用户授予和收回权限。

2. 原理解析

SQL语句使用GRANT和REVOKE语句来将对用户的授权决定通知系统,系统将

授权结果存入数据字典中,在用户提出操作请求时,按照授权情况进行检查,从而决定是

否执行操作。(1)向用户授权的GRANT语句的一般格式是:

犌犚犃犖犜<权限>[,<权限>][犗犖<对象类型><对象名>]

犜犗<用户>[,<用户>][犠犐犜犎犌犚犃犖犜犗犘犜犐犗犖]

这个语句的含义是将指定的操作对象的指定操作权限授予给指定用户。

GRANT语句能够对单个用户授权、多个用户或使用保留字PUBLIC对所有用户授

权;可以授予用户某种权限或全部权限;对用户的权限作用的对象可以是数据库、视图、基本表。如果是授予更新权限,还可以仅是基本表的某个或某些列,其他的操作权限如果

不是整个表,可以通过定义相应的视图,对视图授予权限。

GRANT语句授予用户权限的时候,当对象不同时,可以授予的操作权限也不同。

当对象是属性列视图时,操作权限包括:SELECT、INSERT、UPDATE(列名[,列名])、DELETE、ALLPRIVILLGES(表示前四种权限的总和)。

当对 象 是 基 本 表 时,操 作 权 限 在 上 述 的 基 础 上,还 增 加 了 ALTER、INDEX和

ALLPRIVILLGES这个三 个 权 限。其 中,ALLPRIVILLGES表 示 六 种 权 限 的

总和。

当对象是数据库时,操作权限是CREATTABLE。接受权限的可以是一个或多

个用户,也可以PUBLIC,即全部用户。

WITHGRANTOPTION子句表明,获得某种权限的用户可以把该权限传播给其他

用户。(2)数据库管理员和授权者可以使用REVOKE语句来收回权限,一般格式是:

犚犈犞犗犓犈<权限>[,<权限>][犗犖<对象类型><对象名>]

犉犚犗犕<用户>[,<用户>]

需要注意的 是,当 系 统 收 回 用 户 的 权 限 时,比 如 USER4的 插 入 权 限,如 果 用 户

USER4将此授权给其他用户,比如 USER5,则系统在收回 USER4的 插 入 权 限 时,会 自

动收回USER5的插入权限,也就是说收回权限的操作会级联下去。

Page 45: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

38   数据库系统实验指导教程

另外,表的所有者自动拥有所有权限,而且是不能被取消。(3)循环授权问题。考虑这种情况:当用多次授权的时候,假设A授权给B,B授权给C,这时候C是否

能授权给A呢?实际上,MicrosoftSQLServer是会允许C向A授权这个操作执行的。那么,在C向A授权之后,在由B取消C的权限的时候,由上面的级联删除的原理,A是

否还拥有权限呢?在实际中,MicrosoftSQLServer是如何处理的呢?这是一个值得探

讨的问题。

3. 实验内容

(1)使用GRANT语句来对用户授权,对单个用户或 多 个 用 户 授 权,或 使 用 保 留 字

PUBLIC对所有用户授权。对不同的操作对象包括数据库、视图、基本表等进行不同权限

的授权。(2)使用 WITHGRANTOPTION子句授予用户传播该权限的权利。(3)当在授权时发生循环授权,考察DBS能否发现这个错误。如果不能,结合取消

权限操作,查看DBS对循环授权的控制。(4)使用REVOKE子句收回授权,取消授权的级联反应。

4. 实验步骤

1)要求

在数据库School中建立三个用户USER1、USER2和USER3,它们在数据库中的角

色是PUBLIC。请按以下要求,分别以管理员身份或这三个用户的身份登录到数据库中,进行操作。

(1)授予所有用户对表COURSES的查询权限。(2)授予用户USER1对表STUDENTS插入和更新的权限,但不授予删除权限,并

且授予用户USER1传播这两个权限的权利。(3)允许用户USER2在表CHOICE中插入元组,更新的SCORE列,可以查询除了

SID以外的所有列。(4)用户USER1授予用户USER2对表STUDENTS插入和更新的权限,并且授予

用户USER2传播插入操作的权利。(5)收回对用户USER1对表COURSES查询权限的授权。(6)由上面(2)和(4)的授权,再由用户 USER2对用户 USER3授予表STUDENTS

插入 和 更 新 的 权 限,并 且 授 予 用 户 USER3传 播 插 入 操 作 的 权 利。这 时 候,如 果 由

USER3对USER1授予表STUDENTS的插入和更 新 权 限 是 否 能 得 到 成 功?如 果 能 够

成功,那么如果有用户USER2取消USER3的权限,对USER1会有什么影响?如果再由

DBA取消USER1的权限,对USER2有什么影响?

2)分析与解答

(1)授予所有用户对表COURSES的查询权限,SQL语句如下(见1_5_1.sql):

犌犚犃犖犜犛犈犔犈犆犜

Page 46: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

39   第1章 SQL语言

犗犖犆犗犝犚犛犈犛犜犗犘犝犅犔犐犆

(2)对用户USER1授予对表STUDENTS插入和更新的权限,并且授予传播这两个

权限的权利但不授予删除权限。SQL语句如下(见1_5_2.sql):

犌犚犃犖犜犐犖犛犈犚犜,犝犘犇犃犜犈犗犖犛犜犝犇犈犖犜犛犜犗犝犛犈犚1犠犐犜犎犌犚犃犖犜犗犘犜犐犗犖

在本例子中,用户USER1被授予了插入和更新的权限,而且还具备了可以传播这两

个权限的权利。也就是说,USER1可以通过GRANT语句向其他用户授权。(3)允许用户USER2在表CHOICE中插入元组,更新的SCORE列,可以查询除了

SID以外的所有列。因为没有 与 查 询 权 限 相 关 的 字 段 说 明,要 实 现 上 述 功 能,必 须 先 创 建 一 个 视 图

CHOICEVIEW,SQL语句如下(见1_5_3.sql):

犆犚犈犃犜犈犞犐犈犠犆犎犗犐犆犈犞犐犈犠(犖犗,犛犐犇,犆犐犇,犛犆犗犚犈)

犃犛犛犈犔犈犆犜犖犗,犛犐犇,犆犐犇,犛犆犗犚犈犉犚犗犕犆犎犗犐犆犛

现在,可以在视图上对用户授予合适的权限。SQL语句如下(见1_5_4.sql):

犌犚犃犖犜犛犈犔犈犆犜,犐犖犛犈犚犜,犝犘犇犃犜犈(犛犆犗犚犈)

犗犖犆犎犗犐犆犈犞犐犈犠犜犗犝犛犈犚2

该视图是原来基表的子集,因用户 USER2是不能查询SID列的。而对其他列的权

限授予可以确保该用户在访问该表时拥有相应的权限。(4)用户USER1授予用户USER2对表STUDENTS插入和更新的权限,并且授予

用户USER2传播插入操作的权利。

USER1具备传 播 插 入 和 更 新 表STUDENTS的 权 限,是 由 在(2)使 用 的 WITHGRANTOPTION赋予的。在此操作中,需使用用户名USER1登录数据库,同样是使用

GRANT语句授权给用户USER2。SQL语句如下(见1_5_5.sql):

犌犚犃犖犜犐犖犛犈犚犜,犝犘犇犃犜犈犗犖犛犜犝犇犈犖犜犛犜犗犝犛犈犚2犠犐犜犎犌犚犃犖犜犗犘犜犐犗犖

操作完成后,用户USER2也具备了对表STUDENTS插入和更新的权限。(5)收回用户USER1对表COURSES的查询权限的授权。取消没有传播权利的授权操作比较简单,使用REVOKE语句如下(见1_5_6.sql):

犚犈犞犗犓犈犛犈犔犈犆犜

Page 47: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

40   数据库系统实验指导教程

犗犖犆犗犝犚犛犈犛犉犚犗犕犝犛犈犚1

(6)由上面(2)和(4)的授权,再由用户 USER2对用户 USER3授予表STUDENTS插入 和 更 新 的 权 限,并 且 授 予 用 户 USER3传 播 插 入 操 作 的 权 利。这 时 候,如 果 由

USER3对USER1授予表STUDENTS的插入和更新权限是否能得到成功?

由用户 USER2对 用 户 USER3授 予 表STUDENTS插 入 和 更 新 权 限,并 授 予 传 播

权,以用户USER2的身份登录到数据库,再在数据库中执行SQL的语句如下(见1_5_7.sql):

犌犚犃犖犜犐犖犛犈犚犜,犝犘犇犃犜犈犗犖犛犜犝犇犈犖犜犛犜犗犝犛犈犚3犠犐犜犎犌犚犃犖犜犗犘犜犐犗犖

执行以后,USER3也具备了这两项权利。那么这时候,USER3能不能将这两项权利

授予给USER1呢?虽 然 USER3的 权 利 是 USER2授 予 的,USER2是 由 USER1授 予

的。由USER3再授予 给 USER1,这 是 一 个 逻 辑 上 的 矛 盾,构 成 了 一 个 如 图1.1所 示

的环:

图1.1 循环授权

注意:图1.1中用箭头连接表示双方具有授权关系,箭头由A指向B,表示由A授权

给B。如果用户进行了这样的操作,即构成了循环授权,编译器是否能检查得出来呢?以用

户名USER3登录数据库,输入如下SQL语句(见1_5_8.sql):

犌犚犃犖犜犐犖犛犈犚犜,犝犘犇犃犜犈犗犖犛犜犝犇犈犖犜犛犜犗犝犛犈犚1

执行后编译器提示:“命令已成功完成。”这证明这种逻辑上的矛盾,编译器是检查不

出来的。进一步考虑,如果USER2取消USER3的权限的时候,USER1的权限还存在吗?按

照级联删除的含义,取消 USER3的权限的时候,同时取消所有由 USER3授予的相关权

限。那么,数据库由于USER3曾经授予 USER1这 两 个 权 限 而 取 消 USER1的 权 限 吗?

从逻辑上讲,无论USER1的权限是否被取消,都构成了矛盾。还是回到数据库中,看看

实际上DBS是如何处理这个问题的。输入如下语句(见1_5_9.sql):

犚犈犞犗犓犈犐犖犛犈犚犜,犝犘犇犃犜犈犗犖犛犜犝犇犈犖犜犛

Page 48: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

41   第1章 SQL语言

犉犚犗犕犝犛犈犚3

执行后编译器提示:“服务器:消息4611,级别16,状态1,行1,若要废除可授予的

特权,请在 REVOKE语句中指定CASCADE选项。”。这是DBS对删除操作的一个限制,对于具有传播权利的授权,如果要取消,必须指定

CASCADE选项。那 如 果 加 上 了 CASCADE选 项,结 果 又 会 怎 样 呢?SQL语 句 如 下

(见1_5_10.sql):

犚犈犞犗犓犈犐犖犛犈犚犜,犝犘犇犃犜犈犗犖犛犜犝犇犈犖犜犛犉犚犗犕犝犛犈犚3犆犃犛犆犃犇犈

编译器提示“命令已成功完成。”那究竟这个时候 USER1是否还具有原来的权限呢?

可以直接 查 看 用 户 的 权 限 也 可 以 利 用 USER1身 份 登 录 到 数 据 库 中,对 数 据 库 表

STUDENTS做 更 新 操 作 是 否 成 功。如 果 直 接 查 看 用 户 USER3的 权 限,可 以 发 现

USER3的权限没有了,而USER1的权限仍然存在。那表示在这里并没有实现级联的删

除方式。

那么,这时候再考虑由DBA取消 USER1的权限,那么 USER2是否还存在权限呢?

使用如下语句(见1_5_11.sql):

犚犈犞犗犓犈犐犖犛犈犚犜,犝犘犇犃犜犈犗犖犛犜犝犇犈犖犜犛犉犚犗犕犝犛犈犚1犆犃犛犆犃犇犈

再查看用户的权限,发现USER1和USER2的权限都取消,实现了删除的级联操作。从以上事实可以说明,当USER3对USER1进行授权的操作,对于这一组操作来说,

是不起任何作用的。从DBS的角度看来,USER3与 USER1之间的关系只有 USER1授

权给USER2,再由USER2授权给USER3的关系,而不存在USER3授予USER1的关系。在 MicrosoftServer内部具体是如何处理,用户如果有兴趣,可以查阅更多的相关书籍。

注意:以上的讨论均以在 MicrosoftSQLServer的基础上进行。说明:(1)通过企业管理器在数据库中建立用户的步骤如下:

① 打开企业管理器,选定数据库。

② 右键单击用户图 标,选 择 新 建 数 据 库 用 户 选 项,弹 出 数 据 库 用 户 属 性新 建 用 户

界面。

③ 登录名下拉框中选中新建,会弹出SQLServer登录属性新建登录界面。

④ 在名称中直接输入想要的用户名(如 USER1)。接着选择合适身份验证的方式,在本实验中,可以选择SQLServer身份验证,输入密码,再确定重复输入一次。最后,在

下拉框指定数据库,如本实验的数据库School。语言则选择默认就可以了。按确定回到

数据库用户属性新建用户界面。

Page 49: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

42   数据库系统实验指导教程

⑤ 在用户名下拉框中选中已经新建成功的用户名(如 USER1)。最后,在数据库角

色成员的多 个 复 选 框 中 选 择 合 适 的 选 项。在 本 次 实 验 中,仅 选 中PUBLIC选 项 就 可

以了。(2)先打开企业管理器再进入查询分析器,是以管理员的身份登录数据库,若需要使

用其他用户身份登录,则可以直接打开查询分析器,按要求选择数据库,输入用户名和密

码即可。

5. 习题

(1)授予所有用户对表STUDENTS的查询权限。(2)授予所有用户对表COURSES的查询和更新权限。(3)授予用户 USER1对表TEACHERS的查询,更新工资的权限,且允许 USER1

可以传播这些权限。(4)授予用户USER2对表CHOICES的查询,更新成绩的权限。(5)授予用户USER2对表TEACHERS的可以查询除了工资之外的所有信息。(6)由用户USER1授予用户USER2对表TEACHERS的查询权限,并传播的此项

权限的权利。(7)由用户USER2授予用户USER3对表TEACHERS的查询权限,和传播的此项

权限的权利。再由用户USER3授予用户 USER2上述权限,这样的SQL语句能否成功

得到执行?

(8)取消用户USER1对表STUDENTS的查询权限,考虑由用户USER2的身份对

表STUDENTS进行查询,操作能否成功?为什么?

(9)取消用户USER1和USER2的关于表COURSES的权限。注意:以上各题,若无特别指明,均指由表的所有者授权或取消授权。

实验1.6 空值和空集的处理

1. 实验目的

  认识NULL值在数据库中 的 特 殊 含 义,了 解 空 值 和 空 集 对 于 数 据 库 的 数 据 查 询 操

作,特别是空值在条件表达式中与其他的算术运算符或逻辑运算符的运算中,空集作为嵌

套查询的子查询的返回结果时候的特殊性,能够熟练使用SQL语句来进行与空值,空集

相关的操作。

2. 原理解析

NULL是一个常量,仅在数值和字符串类型的列中有意义,代表的是没有意义或者是不

确定的值。例如,学生选了课程,当成绩没有出来时grade字段的值应该为空;或者工资表

中一个行政人员在课时补贴一栏的值为NULL,因为它不可能有课时补贴的收入。由于NULL值的特殊性,在进行与其相关的操作的时候,可能导致特别的效果。

Page 50: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

43   第1章 SQL语言

(1)对NULL值做算术运算的结果还是NULL。(2)NULL与比较运算符的运算都返回FALSE值。因为NULL值表示没有意义或不确定的值,所以它与任何比较运算符运算的结果,

肯定都是不匹配的。在结果集中,NULL值是不会出现的。这会引起一个特殊的 现 象,当在一个列中有某些元素取空值时,对这个列作>和<=某个非空值的两个查询,得到的

并不是全集的情况。也即是NULL值的存在,会使>和<=的互补性失效。(3)判断是否为空值的要使用ISNULL。在查询分析器中,当需要判断一个值是否为空值的时候,应该采取什么方法呢?因为

NULL与所有比较运算符的运算结果都不匹配,那么无论是=NULL还是=,都是不能

找出取空值的项。正确的写法应该是:ISNULL。当需要返回所有不是空值的项可以用

ISNOTNULL。(4)使用排序ORDERBY情况,NULL值被当作最小值处理。在使用ORDERBY进行排序的时候,有两种方式 ASC(升序)和DESC(降序)。无

论采用其中的哪一种,都涉及一个大小的问题。在这种情况下,NULL被当作最小值来

处理。若按升序排,取空值的元组将最后显示,若按降序排,取空值的元组将最先显示。(5)与在DISTINCT保留字结合使用时,所有的NULL值都被看成是相同。使用DISTINCT保留字的时候,NULL被看成是一个取值,在处理的时候把所有取

NULL的项都看成是一 样 的。例 如SELECTDISTINCTSCOREFROMCHOICES的

时候,在结果集中会有一个项是 NULL。需要注意,NULL值在数据库中被当作一个与

其他确定值不同的一种取值,这并不一定意味着取NULL值的元组在这列的取值在实际

中有与众不同的新的值,而事实上很有可能就是取那些确定值的一种。(6)在GROUPBY的时候,所有取NULL值也被看成是取一个相同值。使用GROUPBY进行排序,取NULL的项不是被忽略,而是将NULL看成是一个

取值,在处理的时候把所有的取NULL的项都看成一样,因而形成一个分组。(7)在集合函数中空值和空集的处理情况。除了COUNT函数在计算元组的时候会把取空值的列的元组计算在内外,其他的集

合函数都是把对应的列的取值为空值的项跳过,不算在集合范围之内。例如,求平均分的

时候,当其中一些元组这一列的值取空值,在计算总分和总人数的时候,这些取空值的都

不算在内。所求出来的平均是所有成绩不是空值的成绩的平均,而不是所有成绩的平均。当集合函数统计的对象是一个空集的时候,COUNT()函数返回0,其他的所有的集

合函数的结果都为NULL值。(8)在嵌套查询中空值与空集的处理情况。在嵌套查询中,如果子查询返回空集,与各种逻辑谓词进行运算,结果各不相同,有如

下五种情况:第一,当使用谓词IN时,形式是exprIN (subquery)时,若由于子查询返回空集,所

以条件的逻辑运算结果取FALSE。第二,当 使 用 比 较 运 算 符 和 谓 词SOME或 ANY 的 时,形 式 是exprθ{SOME|

ANY}(subquery),若子查询返回空集时,外部查询的条件逻辑运算结果取FALSE。

Page 51: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

44   数据库系统实验指导教程

第三,当使用比较谓词ALL时,形式exprθ{ALL}(subquery),当且仅当子查询返

回至少一个值,并 且 比 较 结 果 是FALSE的 时 候,整 个 条 件 的 取 值 为FALSE。否 则,取

TRUE值。当子查询返回空集的时候,无论比较谓词是什么,都取TRUE值。

第四,当使用谓词EXIST时,形式是EXISTS(subquery),当子查询返回空集合的时

候,整个外层的判断条件取FALSE。

第五,当使用NOTEXIST连接子查询的时候,形式是NOTEXISTS(subquery),当子查询返回空集时,外层的判断条件取TRUE,反之取FALSE。

综合以上的情况,再结合子查询返回一个空值的情况(这时返回的不是空集),与各种

谓词连接的运算结果如表1.6所示。

表1.6 谓词与子查询返回空值或返回一个空值的运算结果

Subquery返回空集 Subquery返回一个空值

IN F FNOTIN T F

θSOME|ANY F F

θALL F FEXIST F TNOTEXIST T F

当子查询返回一个空值时,返回结果并非空集,而是返回一个有一个元素的集合,这

个集合中的这个唯一的元素取空值。空集与非空集的不同,连同空值本身的特殊性构成

表1.6的运算结果。(9)连接运算。

考虑对两个集合按某两个列做等值连接的情况,当这两个表的对应列中都存在取空

值的项,那么等值连接的时候,对这些取空值的项是怎么处理的呢?例如,假设对表R和

表D的作R.CID=D.CID的 连 接。如 果 表R存 在CID取 空 值 的 元 组,而 表D也 存 在

CID取空值的元组,这些CID列取空值的元组是否会出现在结果集中呢?通过实验,可

以发现这些项并没有出现在结果集中。这是由于等值连接的运算符是等号,在前面已经

讨论过,NULL值与任何算术比较符比较的结果都是不匹配的,所以在结果集中不会出

现这些CID列取空值的项。

3. 实验内容

通过实验验证在原理解析中分析过的SQLServer对NULL的处理,包括:

在查询的目标表达式中包含空值的运算。

在查询条件中空值与比较运算符的运算结果。

使用ISNULL或ISNOTNULL来判断元组该列是否为空值。

对存在取空值的列按值进行ORDERBY排序。

使用保留字DISTINCT对空值的处理,区分数据库的多种取值与现实中的多种

取值的不同。

Page 52: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

45   第1章 SQL语言

使用GROUPBY对存在取空值的属性值进行分组。

结合分组考察空值对各个集合函数的影响,特别注意对COUNT()和COUNT(列名)的不同影响。

考察结果集是空集时,各个集函数的处理情况。

验证嵌套查询中返回空集的情况下与各个谓词的运算结果。

进行与空值有关的等值连接运算。

4. 实验步骤

1)要求

(1)查询所有选课记录的成绩并将它换算为五分制(满分为5分,合格为3分),注意

SCORE取NULL值的情况。(2)通过查询选修编号10028的课程的学生的人数,其中成绩合格的学生人数,不合

格的学生人数,讨论NULL值的特殊含义。(3)通过实验检验在使用ORDERBY进行排序时,取NULL的项是否出现在结果

中?如果有,在什么位置?

(4)在上面的查询的过程中如果加上保留字DISTINCT会有什么效果呢?

(5)通过实验说明使用分组GROUPBY对取值为NULL的项的处理。(6)结合分组,使用集合函数求每个同学的平均分、总的选课记录、最高成绩、最低成

绩和总成绩。(7)查询成绩小于0的选课记录,统计总数、平均分、最大值和最小值。(8)采用嵌套查询的方式,利用比较运算符和谓词ALL的结合来查询表COURSES

中最少的课时。假设数据库中只有一个记录的时候,使用前面的方法会得到什么结果,为什么?

(9)创建一个学生表S(NO,SID,SNAME),教师表T(NO,TID,TNAME)作为实验

用的表。其中,NO分别是这两个表的主键,其他键允许为空。向S插 入 元 组(1,0129871001,王 小 明)、(2,0129871002,李 兰)、(3,0129871005,

NULL)、(4,0129871004,关红);向T插 入 元 组(1,100189,王 小 明)、(2,100180,李 小)、(3,100121,NULL)、(4,

100128,NULL)。对这两个表作对姓名的等值连接运算,找出既是老师又是学生的人员的学生编号和

教师编号。

2)分析与解答

(1)查询所有选课记录的成绩并将它换算为五分制(满分为5分,合格为3分),注意

SCORE取NULL值的情况。将成绩换算为五分制,只需要对成绩做个整除20的运算就可以得到。在这道题中,

讨论的重点是当一个成绩是NULL值的时候,对它做运算,会得到什么结果呢?

使用简单的SQL语句来对数据库进行上述的查询(见1_6_1.sql):

犛犈犔犈犆犜犛犆犗犚犈,犛犆犗犚犈/20

Page 53: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

46   数据库系统实验指导教程

犉犚犗犕犆犎犗犐犆犈犛

在查询分析 器 中 执 行 之 后,结 果 集 中 可 以 看 到,第79026个 记 录 的SCORE值 是

NULL,对应的运算结果也是NULL。实际的例子说明了一个问题:当一个值是NULL的时候,对它做算术运算,结果仍然是NULL。在这个查询的结果集中,还有很多记录的

SCORE值取NULL的情况,读者可以自己查看验证。(2)通过查询选修编号10028的课程的学生的人数,其中成绩合格的学生人数,不合

格的学生人数,讨论NULL值的特殊含义。查询 选 修 编 号10028的 课 程 的 学 生 的 人 数 只 需 要 一 个 简 单 的 查 询 就 可 以 实 现(见

1_6_2.sql):

犛犈犔犈犆犜犆犗犝犖犜()

犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犆犐犇='10028'

查询其中成绩合格的学生人数也很简单,使用如下的查询(见1_6_3.sql):

犛犈犔犈犆犜犆犗犝犖犜()

犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犛犆犗犚犈>=60犃犖犇犆犐犇='10028'

运行之后可以得到结果为6042和4812。既然选课的总人数已经得到,合格的人数

也已经知道,简单地做个减运算是不是就可以得到不及格的人数6042-4812=1230呢?

通过再一次使用SQL语句来求不及格的人数(见1_6_4.sql)。

犛犈犔犈犆犜犆犗犝犖犜()

犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犛犆犗犚犈<60犃犖犇犆犐犇='10028'

可以看到,结果是755,而不是1230。问题出在哪里呢?从前面表CHOICES的定义

中,可以看到属性SCORE是允许为空的。当元组的SCORE列取空值,也即是NULL的

时候,在上两个查询中,在条件中使用<和>=比较运算符,NULL值与所有的比较运算

符都是不匹配的,所以它都不会出现在结果的统计之中。由此,可以得到,在数据库中,存在1230-755=475个SCORE值不确定(NULL值)的元组。再一次使用SQL语句对数

据库进行查询加以验证(见1_6_5.sql):

犛犈犔犈犆犜犆犗犝犖犜()

犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犛犆犗犚犈犐犛犖犝犔犔犃犖犇犆犐犇='10028'

执行之后可以看到结果的确是475。由此证实前面的推论是对的。注意:要判断是否为NULL值,使用的形式是ISNULL或INNOTNULL。(3)通过实验检验在使用ORDERBY进行排序时,取NULL的项是否出现在结果

中?如果有,在什么位置?

仍然采用前两个例题中的表CHOICES的SCORE列的例子,对它进行排序。首先,

Page 54: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

47   第1章 SQL语言

将成绩按从小到大排列。SQL语句如下(见1_6_6.sql):

犛犈犔犈犆犜犛犆犗犚犈犉犚犗犕犆犎犗犐犆犈犛犗犚犇犈犚犅犢犛犆犗犚犈犃犛犆

查看结果,可以发现,取NULL值的项并没有被忽略,而是排在最前面。这是不是意

味着NULL值在排列的时候被当作是最小值处理呢?可以再通过按降序排列的实验来

验证这种假设是否正确。使用下列语句(见1_6_7.sql):

犛犈犔犈犆犜犛犆犗犚犈犉犚犗犕犆犎犗犐犆犈犛犗犚犇犈犚犅犢犛犆犗犚犈犇犈犛犆

查看结果,取NULL值的项果然是在排在最后面。(4)在上面的查询的过程中如果加上保留字DISTINCT会有什么效果呢?

从NULL值的定义上来看,它是表示不确定或没有意义。那么,两个NULL值就表

示两个不确定或没有意义的值。再进一步,这两个不确定或没有意义的值从DBMS的角

度看来是一样还是不一样呢?如果是一样的话,那DISTINCT作用的结果,就会只有一

个NULL项(如 果 有 取 空 值 的 项,不 管 项 的 数 量 是 多 少)。如 果 不 一 样 的 话,则

DISTINCT作用的 结 果,原 来 有 多 少 个 NULL项,作 用 后 仍 有 那 么 多 个 项。又 或 者,

DBMS在使用保留字的时候干脆把所有取 NULL值的项都忽略掉,连一个 NULL都不

显示在结果集中呢?实验是最好的检验方法,使用下面的SQL语句(见1_6_8.sql):

犛犈犔犈犆犜犇犐犛犜犐犖犆犜犛犆犗犚犈犉犚犗犕犆犎犗犐犆犈犛犗犚犇犈犚犅犢犛犆犗犚犈犇犈犛犆

在前面的实验中,可以看到在数据库中有不少个成绩取NULL值。在使用上述语句

进行查询之后,可以发现在结果集中,最后一个项是取NULL值的。由此可以证明,在前

面的假设中,第一种假设是正确,也即是在DBMS的角度看来,所有的取NULL值的项

都看成是一样的。在这个实验中,还有一个需要注意的问题。在实验的结果中,共有47个不同的结果

项。在一般情况下,这就说明该列的取值有47个不同的取法。然而,它与事实中表示的

意思是不完全一样的。在实际上,所有同学的选课成绩至少有46种不同的成绩,而不一

定有47种。这是因为,NULL值在这里表示不确定,在数据库中认为它是与其他确定值

的项不同。而在实际中,不确定的值很有可能就是那些已经确定的值的其中之一。例如,假设有一个属性是校区,假定这个学校的校区有四个:东校区、西校区、南校区和北校区。存在学生A是这个学校的,然而,由于不确定A是哪个校区的,因而,在数据库中,A的这

个列的取值是NULL。这时候在数据库中,校区这一属性的取值就有五种不同的情况,使用DISTINCT的结果也是有五个不同的取值,但是这并不意味着该学校就有五 个 校

区,事实上只有四个。A也不是去了一个名字为NULL的校区,而是不确定的这四个校

区中的某个。所以,在数据库中的五种取值,不一定对应着事实中有五种情况。这是一个

需要特别注意的地方,也是NULL值的特殊性的一个体现。(5)通过实验说明使用分组GROUPBY对取NULL值的项的处理。通过上个例子的讨论,可以得出结论:NULL值在数据库是当作一个与其他的取值

一样类型的值。而使用分组GROUPBY的时候对取NULL值的项是否也作相同处理

呢?可以通过实验来对此进行验证。考虑按成绩来对表CHOICES进行分组,SQL语句

Page 55: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

48   数据库系统实验指导教程

如下(见1_6_9.sql):

犛犈犔犈犆犜犛犆犗犚犈犉犚犗犕犆犎犗犐犆犈犛犌犚犗犝犘犅犢犛犆犗犚犈

实验证明,查询的结果存在取值为NULL的项,这说明在使用分组的时候取NULL值被当作一个分组。

(6)结合分组,使用集合函数求每个学生的平均分、总的选课记录数、最高成绩、最低

成绩和总成绩,考察取空值的项对集合函数的作用的影响。统计每个学生的成绩,可以先按学生的学号分组,再在每个分组中进行统计,可使用

如下的SQL语句(见1_6_10.sql):

犛犈犔犈犆犜犛犐犇,犃犞犌(犛犆犗犚犈),犆犗犝犖犜(),犆犗犝犖犜(犛犆犗犚犈),犕犃犡(犛犆犗犚犈),犕犐犖(犛犆犗犚犈),犛犝犕(犛犆犗犚犈)

犉犚犗犕犆犎犗犐犆犈犛犌犚犗犝犘犅犢犛犐犇

查看结果,可以发现:学 生 编 号 为844936486的 学 生 有 一 个 选 课 记 录,然 而 成 绩 是

NULL,各个集合函数统计的结果分别为NULL、1、0、NULL、NULL、NULL。这一个学

生只有一个成绩为NULL的记录,在使用COUNT()统计元组的时候,由于存在一个

记录,所以取值1;而对于COUNT(SCORE),统计时将取NULL值的项忽略了,所以取

值0。对于最大值,最小值,在这里都取空值。这是什么原因呢?是因为只有一个元组,所以无论最大最小都 取 自 身 的 值 呢,还 是 因 为 在 统 计 最 大 最 小 值 的 时 候 忽 略 了 所 有 取

NULL值的项,所以统计出来的结果是没有项,因而给了NULL值?事实上是哪一种呢?

再看另一个 项,学 生 编 号 为840545438的 学 生 有 两 个 选 课 记 录,其 中 一 个 的 成 绩 为

NULL,另一个为71,各个集合函数统计的结果分别为71、2、1、71、71、71。从这组结果看来,在计算平均分、总分、最大值、最小值的时候,取NULL值的项被忽略了,没有考虑进去。这

样看来,前面的问题可以得到解答:第一种假设是错误的,第二种假设才是正确的。再看学生编号为821705141的项,各个集合函数统计的结果是90、4、3、99、85、272。

可以看出,该学生有四个选修记录,其中一个成绩为NULL,其他三个成绩最高为99,最

低为85,总分为272,三门平均分为90。可以得出结论:在集合函数中,除了使用COUNT()计算元组时要把取空值的项

计算进去,其他的集合函数都忽略了取空值的项。取平均分的函数也是取非空值的项的

平均,而不是所有项的平均。(7)查询成绩小于0的选课记录,统计总数、平均分、最大值和最小值。按要求写出如下SQL语句进行查询(见1_6_11.sql):

犛犈犔犈犆犜犆犗犝犖犜(),犃犞犌(犛犆犗犚犈),犕犃犡(犛犆犗犚犈),犕犐犖(犛犆犗犚犈)

犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犛犆犗犚犈<0

在数据库中,所有的成绩不小于0。所以,上述的查询得到的结果是个空集。对一个

空集使用集合函数进行统计,得到的结果是怎样的呢?由上面运行得到的结果可以发现,

COUNT()返回值为0,其余的函数返回值均为NULL。(8)采用嵌套查询的方式,利用比较运算符和谓词ALL的结合来查询表COURSES

Page 56: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

49   第1章 SQL语言

中最少的课时。假设数据库中只有一个记录的时候,使用前面的方法会得到什么结果,为什么?

对表COURSES查询最少的课时,考虑用<=ALL来连接子查询,使用下面的SQL语句(见1_6_12.sql):

犛犈犔犈犆犜犇犐犛犜犐犖犆犜犎犗犝犚犉犚犗犕犆犗犝犚犛犈犛犃犛犆1犠犎犈犚犈犎犗犝犚<=犃犔犔(

犛犈犔犈犆犜犎犗犝犚犉犗犚犕犆犗犝犚犛犈犛犃犛犆2犠犎犈犚犈犆1.犆犐犇<>犆2.犆犐犇

在数据库School执行得到的结果是18。当数据库中只有一个记录的时候,使用上面的语句进行查询。由于数据库中只有一

个元组,子查询的判断条件C1.CID<>C2.CID这个条件永远都不可能成立。因而子查

询返回的是一个空集。外部查询判断 HOUR<=ALL(空集)的结果。在逻辑上,一个元

素是无法与一个 空 集 的 元 素 作 比 较 的。针 对 这 个 问 题,DBS做 了 这 样 处 理:返 回 的 是

TRUE。这样的处理在实际应用也是适当的,如本题中,判断条件返回TRUE,即数据库

中唯一的这个元组将被选出来。即使出现这个唯一的元组的 HOUR取NULL值,把它

作为结果输出与使用集合函数 MAX(HOUR)得到的结果是一样的。(9)按 题 目 要 求 创 建 一 个 学 生 表 S(NO,SID,SNAME),教 师 表 T(NO,TID,

TNAME)作为实验用的表,插入数据的操作比较简单,由读者自己完成。现在,考虑对这两个表作对姓名的等值连接运算,找出既是老师又是学生的人员的学

生编号和教师编号,使用的SQL语句如下(见1_6_13.sql):

犛犈犔犈犆犜犜犐犇,犛犐犇,犛犖犃犕犈犉犚犗犕犜,犛犠犎犈犚犈犜.犜犖犃犕犈=犛.犛犖犃犕犈

执行之后可以得到结果为一个项(0129871001,100189,王小明)。对两个表数据进行

分析,可以发现除了两个表都有一个姓名为王小明的元组外,S表中有一个姓名为空值的

元组,而T表中有两个。虽然这些元组的姓名在数据库中的取值都是空值 NULL,然而

在对这两张表作等值连接的时候,这些项都被忽略了。从实验中以得出结论,在作对某两个列的等值连接的时候,该列的NULL值的项都

被忽略。两个属于不同表并且对应列都取NULL值的项是不会发生连接的,也不会出现

在结果集中。

5. 习题

按下列要求,写出SQL语句,通过实验验证SQLServer对NULL值和空集的处理

方式。(1)查询所有课程记录的上课节时(数据库中为每周节时),以一学期18周计算每个

Page 57: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

50   数据库系统实验指导教程

课程的总学时,注意 HOUR取NULL值的情况。(2)通过查询选修课程C++的学生的人数,其中成绩合格的学生人数,不合格的学生

人数,讨论NULL值的特殊含义。

(3)查询选修课程C++的学生的编号和成绩,使用ORDERBY按成绩进行排序时,

取NULL的项是否出现在结果中?如果有,在什么位置?

(4)在上面的查询的过程中如果加上保留字DISTINCT会有什么效果呢?

(5)按年级对所有的学生进行分组,能得到多少个组?与现实的情况有什么不同?

(6)结合分组,使用集合函数求每个课程选修的学生的平均分、总的选课记录数、最

高成绩和最低成绩,讨论考察取空值的项对集合函数的作用的影响。

(7)采 用 嵌 套 查 询 的 方 式,利 用 比 较 运 算 符 和 谓 词 ALL 的 结 合 来 查 询 表

STUDENTS中最晚入学的学生年级。当存在GRAND取空值的项时,考虑可能出现的

情况,并解释原因。

(8)将操作步骤中的表的数据进行更新,使得表S中,NO为2、3的记录的SID列取

NULL值,T表的NO为4的记录的TID取NULL值,NO为3的TID取0129871005。

然后,对这两个表按T.TID=S.SID作等值连接运算,找出编号相同的学生和教师的姓

名,并分析原因。

习题答案

实验 1.1

  (1)CREATETABLECUSTOMER(CIDCHAR(8)UNIQUE,CNAMECHAR(20),CITYCHAR(8),

DISCNTINT,

PRIMARYKEY(CID))

CREATETABLEAGENTS(AIDCHAR(8)UNIQUE,ANAMECHAR(20),CITYCHAR(8),

PERCENTSFLOAT,

PRIMARYKEY(AID))

CREATETABLEPRODUCTS(PIDCHAR(8)UNIQUE,PNAMECHAR(20),PRIMARYKEY(PID))

(2)CREATETABLEORDERS(ORDNACHAR(8)UNIQUE,MONTHINT,CIDCHAR(8),AIDCHAR(8),

PIDCHAR(8),QTYINT,DOLLARSFLOAT,PRIMARYKEY (ORDNA),

FOREIGNKEY(CID)REFERENCESCUSTOMER,FOREIGN KEY(AID)

REFERENCESAGENTS,FOREIGNKEY(PID)REFERENCESPRODUCTS)

Page 58: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

51   第1章 SQL语言

(3)ALTERTABLEPRODUCTSADDCITYCHAR(8)

ALTERTABLEPRODUCTSADDQUANTITYINTALTERTABLEPRODUCTSADDPRICEFLOAT

(4)CREATEINDEXXSNOONCUSTOMER(CID)

CREATEINDEXXSNOONAGENTS(AID)

CREATEINDEXXSNOONPRODUCTS(PID)

CREATEINDEXXSNOONORDERS(ORDNA)(5)DROPINDEXCUSTOMER.XSNODROPINDEXAGENTS.XSNODROPINDEXPRODUCTS.XSNODROPINDEXORDERS.XSNO

实验 1.2

(1)SELECTFROMCOURSES(2)SELECTSIDFROMCHOICES(3)SELECTCIDFROMCOURSESWHEREHOUR<88(4)SELECTSIDFROM CHOICESGROUPBYSID HAVINGSUM(SCORE)

>400(5)SELECTCOUNT(CID)FROMCOURSES(6)SELECTCID,COUNT(SID)ROMCHOICESGROUPBYCID(7)SELECTSIDFROMCHOICESWHERESCORE>60GROUPBYSIDHAVINGCOUNT(CID)>2

(8)SELECTSID,COUNT(CID),AVG(SCORE)FROM CHOICESGROUPBYSID

(9)SELECTSTUDENTS.SID,SNAMEFROMSTUDENTS,CHOICES,COURSESWHERE STUDENTS.SID = CHOICES.SID AND CHOICES.CID =COURSES.CIDANDCOURSES.CNAME='Java'

(10)SELECTCHOICES.SID,CHOICES.SCOREFROMCHOICES,STUDENTSWHERESNAME='sssht'ANDCHOICES.SID=STUDENTS.SIDSELECTCID,SCOREFROMCHOICESWHERESIDIN(

SELECTSTUDENTS.SIDFROMSTUDENTSWHERESNAME='sssht')(11)SELECTC1.CNAMEFROMCOURSESASC1,COURSESASC2

WHEREC1.HOUR>C2.HOURANDC2.CNAME='C++'

(12)SELECTSID,SNAMEFROMSTUDENTSWHERESIDIN(

SELECTC1.SIDFROMCHOICESASC1,CHOICESASC2WHEREC1.SCORE>C2.SCOREANDC1.CID=C2.CIDANDC2.SID=(SELECTSIDFROMSTUDENTSWHERESNAME='znkoo')

ANDC1.CID=(SELECTCIDFROMCOURSESWHERECNAME='C++'))

Page 59: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

52   数据库系统实验指导教程

(13)SELECTSNAMEFROMSTUDENTSWHEREGRADEIN(

SELECT GRADE FROM STUDENTS WHERE SID IN ('883794999','850955252'))

(14)SELECTSNAMEFROMSTUDENTSWHERESIDNOTIN(SELECT SID FROM CHOICES WHERE CID=(SELECT CID FROMCOURSESWHERECNAME='Java'))

(15)SELECTFROM COURSES WHERE HOUR<=ALL(SELECT HOURFROMCOURSES)

(16)SELECTCHOICES.TID,CIDFROMCHOICESWHERENOTEXISTS(

SELECTFROMTEACHERSWHERETEACHERS.SALARY>=(SELECT SALARY FROM TEACHERS WHERE TEACHERS.TID=CHOICES.TID))

(17)SELECTSIDFROMCHOICESWHERESCORE=(SELECTMAX(SCORE)FROMCHOICESWHERECID=(

SELECTCIDFROMCOURSESWHERECNAME='ERP'))(18)SELECTCNAMEFROMCOURSESWHERECIDNOTIN(

SELECTCIDFROMCHOICES)(19)SELECTCNAMEFROMCOURSESWHERECID=SOME(

SELECTCIDFROMCHOICESWHERETID=SOME(

SELECTTIDFROMCOURSES,CHOICESWHERECNAME='UML'ANDCOURSES.CID=CHOICES.CID))

(20)SELECTSNAMEFROMSTUDENTSWHERENOTEXISTS(

SELECTFROMCHOICESASC1WHERENOTEXISTS(

SELECT FROM CHOICESASC2 WHEREC2.SID=STUDENTS.SIDANDC2.CID=C1.CIDANDC2.TID='200102901'))

(21)SELECTSID FROM CHOICES,COURSES WHERE COURSES.CID=CHOICES.CIDANDCOURSES.CNAME='database'

UNIONSELECTSID FROM CHOICES,COURSES WHERE COURSES.CID=CHOICES.CIDANDCOURSES.CNAME='UML'

(22)SELECTX.SIDFROMCHOICESASX,CHOICESASYWHERE(X.CID=(SELECTCIDFROM COURSES WHERECNAME='database')

ANDY.CID=(SELECTCIDFROMCOURSESWHERECNAME='UML'))

ANDX.SID=Y.SID(23)SELECTX.SIDFROMCHOICESASX,CHOICESASY

WHERE(X.CID=(SELECTCIDFROM COURSES WHERECNAME=

Page 60: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

53   第1章 SQL语言

'database'))

ANDX.SID=Y.SIDANDNOT(Y.CID=(SELECTCIDFROMCOURSESWHERECNAME='UML'))

实验 1.3

(1)INSERTINTOSTUDENTS(SID,SNAME)

VALUES('800022222','WangLan')(2)INSERTINTOTEACHERSVALUES('200001000','LXL','s4zrck@pew.net','3024')

(3)UPDATETEACHERSSETSALARY=4000WHERETID= '200010493'

(4)UPDATETEACHERSSETSALARY=2500WHERESALARY<2500

(5)UPDATECHOICESSETTID=(SELECTTIDFROMTEACHERSWHERETNAME= 'rnupx')

WHERETID= '200016731'

(6)UPDATESTUDENTSSETGRADE=2001WHERESID= '800071780'

(7)DELETEFROMCOURSESWHERECIDNOTIN(SELECTCIDFROMCHOICESGROUPBYCID)

(8)DELETEFROMSTUDENTSWHEREGRADE<1998

(9)DELETEFROMSTUDENTSWHERESIDNOTIN(SELECTSIDFROMCHOICESGROUPBYSID)

(10)DELETEFROMCHOICESWHERESCORE<60

实验 1.4

(1)CREATEVIEWVIEWCAS

SELECT CHOICES.NO,CHOICES.SID,CHOICES.TID,COURSES.CNAME,CHOICES.SCOREFROMCHOICES,COURSESWHERECHOICES.CID=COURSES.CID

Page 61: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

54   数据库系统实验指导教程

(2)CREATEVIEWVIEWSASSELECTCHOICES.NO,STUDENTS.SNAME,CHOICES.TID,CHOICES.CID,CHOICES.SCOREFROMCHOICES,STUDENTSWHERECHOICES.SID=STUDENTS.SID

(3)CREATEVIEWS1(SID,SNAME,GRADE)ASSELECTSTUDENTS.SID,STUDENTS.SNAME,STUDENTS.GRADEFROMSTUDENTSWHEREGRADE>1998

(4)SELECTFROMVIEWSWHERESNAME= 'uxjof'

(5)SELECTSID,SCOREFROMVIEWCWHERECNAME= 'UML'

(6)INSERTINTOS1VALUES('60000001','Lily',2001)

(7)定义:

犆犚犈犃犜犈犞犐犈犠犛1(犛犐犇,犛犖犃犕犈,犌犚犃犇犈)犃犛犛犈犔犈犆犜犛犐犇,犛犖犃犕犈,犌犚犃犇犈犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈犌犚犃犇犈>1998犠犐犜犎犆犎犈犆犓犗犘犜犐犗犖

插入元组:

犐犖犛犈犚犜犐犖犜犗犛1犞犃犔犝犈犛('60000001','犔犻犾狔',1997)

执行结果:

服务器:消息550,级别16,状态1,行1试图进行的插入或更 新 已 失 败,原 因 是 目 标 视 图 或 者 目 标 视 图 所 跨 越 的 某 一 视 图 指 定 了 犠犐犜犎犆犎犈犆犓犗犘犜犐犗犖,而该操作的一个或多个结果行又不符合犆犎犈犆犓犗犘犜犐犗犖约束的条件。语句已终止。

结果讨论:加入了 WITHCHECKOPTION子句后,使得所有的对视图的插入或更新

操作都必须满足定义视图时指明的条件,在本题就是GRADE>1998。题目中要插入的元组

并不满足这个条件,GRADE=1997<1998。所以在本题中插入这个元组是不成功的。删除元组:

犇犈犔犈犜犈犉犚犗犕犛1犠犎犈犚犈犌犚犃犇犈=1999

执行结果:

服务器:消息547,级别16,状态1,行1犇犈犔犈犜犈语句与犆犗犔犝犕犖犚犈犉犈犚犈犖犆犈约束 '犉犓_犆犎犗犐犆犈犛_犛犜犝犇犈犖犜犛'冲突。该冲突发生于数据库 '犛犮犺狅狅犾',表 '犆犎犗犐犆犈犛',犮狅犾狌犿狀'狊犻犱'。语句已终止。

结果讨论:虽然要删除的元组并没有违反视图定义中的约束(GRADE=1999>1998),

Page 62: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

55   第1章 SQL语言

但是,由于基表STUDENTS和表CHOICES之间存在引用完整性的约束,而将GRADE=1999的元组删除将违反了它们之间的引用完整性约束,所以出现了上面的错误。

(8)UPDATEVIEWSSETSCORE=SCORE+5WHERESNAME= 'uxjof'

(9)DROPVIEWVIEWCDROPVIEWVIEWSDROPVIEWS1

实验 1.5

(1)GRANTSELECTONSTUDENTSTOPUBLIC(2)GRANTSELECT,UPDATEONCOURSESTOPUBLIC(3)GRANTSELECT,UPDATE(SALARY)ONTEACHERSTOUSER1

WITHGRANTOPTION(4)GRANTSELECT,UPDATE(SCORE)ONCHOICESTOUSER2(5)CREATEVIEWTVASSELECTTID,TNAME,EMAIL,SALARYFROMTEACHERSGRANTSELECTONTVTOUSER2

(6)以用户USER1身份登录数据库后,执行:

GRANTSELECTONTEACHERSTOUSER2WITHGRANTOPTION(7)用户USER2身份登录数据库后,执行:

GRANTSELECTONTEACHERSTOUSER3WITHGRANTOPTION以用户USER3身份登录数据库后,执行:

GRANTSELECTONTEACHERSTOUSER2WITHGRANTOPTION编译器都提示正常执行。

(8)REVOKESELECTONTEACHERSFROMUSER1CASCAD操作不成功,取消授权操作存在级联效应。

(9)REVOKESELECT,UPDATEONCOURSESFROMUSER1,USER2

实验 1.6

(1)SELECTCID,HOUR18FROM COURSES对 NULL做 算 术 运 算 结 果 为

NULL。(2)参考实验1.6的实验步骤中(2)。(3)NULL的项出现在结果中,被当作最小值看待。(4)SELECTDISTINCT,SCOREFROMCHOICESWHERECID=(SELECTCIDFROMCOURSESWHERECNAME='C++')ORDERBYSCORE成绩为NULL的项排在最后面。

Page 63: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

56   数据库系统实验指导教程

(5)SELECTDISTINCTGRADEFROMSTUDENTSGROUPBYGRADE得到15个组,现实中有14个年级。

(6)SELECTAVG(SCORE),COUNT(),MAX(SCORE),MIN(SCORE)

FROMCHOICESGROUPBYCID(7)SELECTGRADEFROMSTUDENTSWHEREGRADE>=ALL(SELECTGRADEFROMSTUDENTS)

(8)SELECTCOUNT()FROMS,TWHERET.TID=S.SID

Page 64: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

数据库的完整性控制

C H A P T E R 2

第2章

数据完整性(DataIntegrity)是 指 数 据 的 精 确 性(accuracy)和 可 靠 性

(reliability),它是防止数据库中存在不符合语义规定的数据和防止因错误

信息的输入输出造成无效操作或错误信息而提出的,保证在一个应用程序

更新数据的过程中数据的语义正确性。数据完整性主要分为三类:实体完整性(EntityIntegrity)、参 照 完 整

性(ReferentialIntegrity)以及用户定义的完整性(UserdefinedIntegrity),其中用户定义的完整性主要包括域完整性(DomainIntegrity)和其他自定

义完整性。为维护数据库的完整性,数据库管理系统(DBMS)必须提供一种机制

来检查数据库中的数据,看其是否满足语义规定的条件。这些加在数据库

数据之上的语义约束条件称为数据库完整性规则,它们作为模式的一部分

存入数据库中。完整性控制是指对数据库进行更新操作,要遵守完整性规 则,才 能 保

证数据的语义是正确的,防止数据库中存在不符合语义的数据。目的是在

合法用户访问数据库的过程中,保证数据的正确性和准确性。完整性检查

机制是指在DBMS中检查数据是否满足完整性条件的机制。由DBMS在

执行更新动作时,检查是否满足预定的完整性约束条件,来进行控制。广

义的完整性控制包括故障恢复、并发控制。而一般所指的完整性控制是指

基于数据库的完整性约束规则,如:实体完整性、引用完整性等。SQL中

两种主要的数据完整性控制机制是指完整性约束规则的定义和检查以及

触发器(Trigger)机制。完整性控制机制应具有三个功能:(1)定义功能,即提供定义完整性约束条件的机制;(2)检查功能,即检查用户发出的操作请求是否违背了约束条件;(3)如果发现用户操作请求使数据违背了完整性约束条件,则采取一

定的动作来保证数据的完整性。完整性控制机制的工作原理基本上分为两类,一种是定义完整性时就

Page 65: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

58   数据库系统实验指导教程

立刻进行检查的,例如实体完整性的定义;另外一种是定义完整性之后进行检查的,例如

参照完整性的定义。

SQLServer提供了一些 工 具 来 帮 助 用 户 实 现 数 据 完 整 性,其 中 最 主 要 的 是:规 则

(Rule)、默认值(Default)、约束(Constraint)和触发器(Trigger)。

实验2.1 实体完整性

1. 实验目的

  学习实体完整性的建立,以及实践违反实体完整性的结果。

2. 原理解析

1)实体完整性(EntityIntegrity)定义

实体完整性规定表的每一行在表中是唯一的实体。SQL语法中,表中的 UNIQUE、

PRIMARYKEY和IDENTITY约束就是实体完整性的体现。实体完整性规则:每个关系中主码的任何属性不能取空值。

注意:空值为NULL,不是0,也不是空格,而是一个“不知道”或“不确定”的数据值。

2)实施完整性检查的时机

实施完 整 性 规 则 检 查 的 时 机 分 为 立 即 检 查 和 延 迟 检 查(ImmediateordeferredChecking),只有选择正确的检查时机才能保证语义的正确性,即保证数据的完整性。

例如,“转账”事务的完整性控制条件是:转出账户A和转入账户B的余额之和保持

不变。转账动作是:A减金额,B加金额。

如果在更新A动作发生后立即启动检查就没有意义,应该延迟到B更新结束后才检

查完整性条件。

实体完整性规则检 查 的 时 机 是 立 即 检 查 的,而 参 照 完 整 性 和 触 发 器 一 般 都 是 延 迟

检查。

3)事务处理

事务是一组数据库操作的集合,这些操作要么一起成功,要么一起失败。操作的提交

或回退是一同生效的。事务处理的概念对维护数据的完整性和一致性是十分重要的。数据库操作(如INSERT、UPDATE和DELETE)如果是一个事务中的操作,那么它

们要在 事 务 控 制 之 下 完 成。数 据 库 对 象 的 beginTransaction、commitTransaction和

rollbackTransaction方法分别用来启动、提交和回退事务。

3. 实验内容

(1)在数据库School中建立表Stu_Union,进行主键约束,在没有违反实体完整性的

前提下插入并更新一条记录(主键的SQL定义语法请参见实验1.1)。(2)演示违反实体完整性的插入操作。

Page 66: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

59   第2章 数据库的完整性控制

(3)演示违反实体完整性的更新操作。(4)演示事务的处理,包括事务的建立、处理以及出错时的事务回滚。(5)通过建立Scholarship表,插入数据,演示当与现有的数据环境不等时,无法建立

实体完整性以及参照完整性。

4. 实验步骤

以系统管理员或sa用户登录进入查询分析器,在查询分析器窗口中输入如下命令,运行并观察结果。

(1)在查询分析器中输入如下SQL语句(见2_1_1.sql):

犝犛犈犛犮犺狅狅犾

犆犚犈犃犜犈犜犃犅犔犈犛狋狌_犝狀犻狅狀(狊狀狅犆犎犃犚(5)犖犗犜犖犝犔犔犝犖犐犙犝犈,

狊狀犪犿犲犆犎犃犚(8),

狊狊犲狓犆犎犃犚(1),

狊犪犵犲犐犖犜,

狊犱犲狆狋犆犎犃犚(20),

犆犗犖犛犜犚犃犐犖犜犘犓_犛狋狌_犝狀犻狅狀犘犚犐犕犃犚犢犓犈犢(狊狀狅));

犻狀狊犲狉狋犛狋狌_犝狀犻狅狀狏犪犾狌犲狊('10000','王敏','1',23,'犆犛');

犝犘犇犃犜犈犛狋狌_犝狀犻狅狀犛犈犜狊狀狅=''犠犎犈犚犈狊犱犲狆狋='犆犛';

犝犘犇犃犜犈犛狋狌_犝狀犻狅狀犛犈犜狊狀狅= '95002'犠犎犈犚犈狊狀犪犿犲='王敏';

狊犲犾犲犮狋犳狉狅犿狊狋狌_狌狀犻狅狀;

得到结果如图2.1所示。

图2.1 插入一条记录

分析:成功建立表,令sno为主键。插入与更新操作都没有违反实体完整性。思考:为什么sno置为''没有违反非空的约束?

(2)在查询分析器中输入如下SQL语句(见2_1_2.sql):

犝犛犈犛犮犺狅狅犾

犻狀狊犲狉狋犛狋狌_犝狀犻狅狀狏犪犾狌犲狊('95002','王嘉','1',23,'犆犛');

得到结果如图2.2所示。

图2.2 违反主键的唯一性属性

分析:违反了主键的唯一性属性,将破坏实体完整性,系统中止操作。(3)在查询分析器中输入如下SQL语句(见2_1_3.sql):

Page 67: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

60   数据库系统实验指导教程

犝犛犈犛犮犺狅狅犾

犝犘犇犃犜犈犛狋狌_犝狀犻狅狀犛犈犜狊狀狅=犖犝犔犔犠犎犈犚犈狊狀狅='95002';

结果如图2.3所示。

图2.3 违反主键的非空属性

分析:违反了主键的非空属性,将破坏实体完整性,系统中止操作。(4)

① 在查询分析器中输入如下SQL语句(见2_1_4.sql):

犝犛犈犛犮犺狅狅犾犛犈犜犡犃犆犜_犃犅犗犚犜犗犖犅犈犌犐犖犜犚犃犖犛犃犆犜犐犗犖犜1犻狀狊犲狉狋犻狀狋狅狊狋狌_狌狀犻狅狀狏犪犾狌犲狊('95009','李勇','犕',25,'犈犈');

犻狀狊犲狉狋犻狀狋狅狊狋狌_狌狀犻狅狀狏犪犾狌犲狊('95003','王浩','0',25,'犈犈');

犻狀狊犲狉狋犻狀狋狅狊狋狌_狌狀犻狅狀狏犪犾狌犲狊('95005','王浩','0',25,'犈犈');

狊犲犾犲犮狋犳狉狅犿狊狋狌_狌狀犻狅狀;

犆犗犕犕犐犜犜犚犃犖犛犃犆犜犐犗犖犜1

结果如图2.4所示。

图2.4 插入一些记录

注意:当SETXACT_ABORT为 ON 时,如果 TransactSQL语句产生运行错误,整个事务将终止并回滚。为 OFF时,只回滚产生错误的 TransactSQL语句,而事务将

继续进行处理。编译错误(如语法错误)不受SETXACT_ABORT的影响。对于大多数

OLEDB提供程序(包括SQLServer),隐性或显式事务中的数据修改语句必须将 XACT_

ABORT设置为 ON。唯一不需要该选项的情况是提供程序支持嵌套事务时。

② 在查询分析器中输入如下SQL语句(见2_1_5.sql):

犝犛犈犛犮犺狅狅犾犛犈犜犡犃犆犜_犃犅犗犚犜犗犖犅犈犌犐犖犜犚犃犖犛犃犆犜犐犗犖犜2犻狀狊犲狉狋犻狀狋狅狊狋狌_狌狀犻狅狀狏犪犾狌犲狊('95007','李明','犕',25,'犈犈');

狊犲犾犲犮狋犳狉狅犿狊狋狌_狌狀犻狅狀;

犻狀狊犲狉狋犻狀狋狅狊狋狌_狌狀犻狅狀狏犪犾狌犲狊('95009','李进','犉',22,'犆犛');

犆犗犕犕犐犜犜犚犃犖犛犃犆犜犐犗犖犜2

结果如图2.2所示。

Page 68: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

61   第2章 数据库的完整性控制

分析:插入的数据违反实体完整性,插入失败,事务回滚。

③ 在查询分析器中输入如下SQL语句(见2_1_6.sql):

犝犛犈犛犮犺狅狅犾狊犲犾犲犮狋犳狉狅犿狊狋狌_狌狀犻狅狀;

结果如图2.4所示。分析:虽然“insertintostu_unionvalues('95007','李明','M',25,'EE')”这个插入并不

出错,但是由于作为一个事务,T2中的操作要么一起成功,要么一起失败,所以当T2失

败时,整个事务回滚到初始状态。(5)

① 在查询分析器中输入如下SQL语句(见2_1_7.sql):

犝犛犈犛犮犺狅狅犾犆犚犈犃犜犈犜犃犅犔犈犛犮犺狅犾犪狉狊犺犻狆(

犕_犐犇狏犪狉犮犺犪狉(10),犛狋狌_犻犱犮犺犪狉(10),犚_犿狅狀犲狔犻狀狋)

犻狀狊犲狉狋犻狀狋狅犛犮犺狅犾犪狉狊犺犻狆狏犪犾狌犲狊('0001','700000',5000)

犻狀狊犲狉狋犻狀狋狅犛犮犺狅犾犪狉狊犺犻狆狏犪犾狌犲狊('0001','800000',8000)

狊犲犾犲犮狋犳狉狅犿犛犮犺狅犾犪狉狊犺犻狆

结果如图2.5所示。

② 在查询分析器中输入如下SQL语句(见2_1_8.sql):

犝犛犈犛犮犺狅狅犾犃犔犜犈犚犜犃犅犔犈犛犮犺狅犾犪狉狊犺犻狆犪犱犱犆狅狀狊狋狉犪犻狀狋犘犓_犛犮犺狅犾犪狉狊犺犻狆犘狉犻犿犪狉狔犽犲狔(犕_犐犇)

结果如图2.6所示。

图2.5 在Scholarship表中插入数据 图2.6 数据环境不满足 M_ID成为主键

分析:当前的数据环境不满足 M_ID成为主键,因为数据列 M_ID不满足实体完整性。

③ 在查询分析器中输入如下SQL语句(见2_1_9.sql):

犝犛犈犛犮犺狅狅犾

犃犔犜犈犚犜犃犅犔犈犛犮犺狅犾犪狉狊犺犻狆犪犱犱

犆狅狀狊狋狉犪犻狀狋犉犓_犛犮犺狅犾犪狉狊犺犻狆犉狅狉犲犻犵狀犽犲狔(犛犜犝_犐犇)狉犲犳犲狉犲狀犮犲狊犛犜犝犇犈犖犜犛(狊犻犱)

结果如图2.7所示。

图2.7 创建参照完整性失败

Page 69: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

62   数据库系统实验指导教程

分析:由于Schoolship中的数据,不满足STU_ID和STUDENTS表中的sid的对

应性,所以创建参照完整性失败。

5. 习题

(1)在School数 据 库 中 建 立 一 张 新 表 Class,包 括 Class_id(varchar(4)),name(varchar(10)),Department(varchar(20))三个列,并约束Class_id为主键。

(2)创建事务T3,在事务中插入一个元组('00001','01CSC','CS'),并在T3中嵌套创

建事务T4,T4也插入和T3一样的元组,编写代码测试,查看结果。

实验2.2 参照完整性

1. 实验目的

  学习建立外键,以及利用FOREIGNKEY…REFERENCES子句以及各种约束保证

参照完整性。

2. 原理解析

1)参照完整性(ReferentialIntegrity)

参照完整性是指两个表的主关键字和外关键字的数据对应一致。它确保了有主关键

字的表中对应其他表的外关键字的行存在,即保证了表之间的数据的一致性,防止了数据

丢失或无意义的数据在数据库中扩散。参照完整性是建立在外关键字和主关键字之间或

外关键字和唯一性关键字之间的关系上的。参照完整性规则:关系R的外来码取值必须是关系S中某个元组的主码值,或者可

以是一个“空值”。定义外键时定义参照完整性、约束参照表A和被参照表B。对于违反参照完整性的

操作有时候并不是 简 单 拒 绝 执 行,而 是 接 受 该 操 作,同 时 执 行 必 要 的 附 加 操 作。DBMS提供机制来定义是否必须制定外键的具体值而非空值。主键列和外键列可以有不同的名

字,空值的要求也可以不一致,默认值也可以不同,但数据类型必须相同。

2)SQLServer中完整性的体现

在SQLServer中参照完整性作用表现在如下几个方面:

禁止在从表中插入包含主表中不存在的关键字的数据行。

禁止会导致从表中的相应值孤立的主表中的外关键字值改变。

禁止删除在从表中的有对应记录的主表记录。

3)SQL语句中删除和插入基本关系元组

(1)在被参照关系中删除元组的三种控制方式:

级联删除(CASCADES):将参照关系中与被参照关系中要删除元组主键值相同

的元组一起删除。

受限删除 (RESTRICTED):只有参照关系中没有元组与被参照关系中要删除元

Page 70: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

63   第2章 数据库的完整性控制

组主键值相同时才执行删除操作,否则拒绝。

置空值删除(SETNULL):删除被参照关系中的元组,同时将参照关系中相应元

组的外键值置为空。(2)在参照关系中插入元组。

受限插入:只有被参照关系中有元组与参照关系中要插入元组外键值相同时,才

执行插入操作,否则拒绝。

递归插入:插入元组外键值在被参照关系中没有元组相同,则首先向被参照关系插

入元组,其主键值等于参照关系插入元组的外键值,然后再向参照关系插入元组。

4)DBMS对参照完整性进行检查的四种情况

(1)在四种情况下DBMS要进行检查,分别是对参照表进行插入和修改以及对被参

照表进行删除和修改。(2)SQLServer四种情况违反参照完整性的处理方法如表2.1所示。

表2.1 四种违反参照完整性的处理方法

相关操作 INSERT DELETE UPDATE

被参照表 不需要检查

ONDELETE…(用 户 显 示 定 义

的方式,提供两种:cascade和noaction)Ordefault(系统默认的方

式noaction)

ONUPDATE…(用 户 显 示 定 义 的

方 式,提 供 两 种:cascade 和 noaction)Ordefault(系 统 默 认 的 方 式

noaction)

参照表 拒绝执行 不需要检查 拒绝执行

5)参照完整性的特殊问题

(1)表的自参照问题。例:

犮狉犲犪狋犲狋犪犫犾犲犲犿狆犾狅狔犲犲(犈犿狆_犻犱犻狀狋犖犗犜犖犝犔犔犘犚犐犕犃犚犢犓犈犢,

 犲犿狆_狀犪犿犲狏犪狉犮犺犪狉(30)犖犗犜犖犝犔犔,

 犕犵狉_犻犱犻狀狋犖犗犜犖犝犔犔犚犈犉犈犚犈犖犆犈犛,犲犿狆犾狅狔犲犲(犲犿狆_犻犱))

问题1:无法定义。处理方法:先用createtable创建主键约束,再用altertable创建外键约束。问题2:容易造成无法启动的情况。(系统通过事务完毕后再检查)(2)两张表互相参照的问题。问题1:无法定义。处理方法:同表的自参照问题的解决方法相同,详见随后的实验步骤中的(10)”。问题2:容易造成无法启动的情况。(系统通过事务完毕后再检查)(3)既是外键又是主键中的属性。处理方法:既要遵从实体完整性也要遵从参照完整性。

3. 实验内容

(1)为演示参照完整性,建立表Course,令cno为其主键,并在Stu_Union中插入数

据。为下面的实验步骤做预先准备。

Page 71: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

64   数据库系统实验指导教程

(2)建立表SC,令sno和cno分别为参照Stu_Union表以及Course表的外键,设定

为级联删除,并令(sno,cno)为其主键。在不违反参照完整性的前提下,插入数据。(3)演示违反参照完整性的插入数据。(4)在Stu_Union中删除数据,演示级联删除。(5)在Course中删除数据,演示级联删除。(6)为了演示多重级联删除,建立Stu_Card表,令stu_id为参照Stu_Union表的外

键,令card_id为其主键,并插入数据。(7)为了演示多重级联删除,建立ICBC_Card表,令stu_card_id为参照Stu_Card表

的外键,令bank_id为其主键,并插入数据。(8)通过删除students表中的一条记录,演示三个表的多重级联删除。(9)演示事务中进行多重级联删除失败的处理。修改ICBC_Card表的外键属性,使

其变为OndeleteNoaction,演示事务中通过删除students表中的一条记录,多重级联删

除失败,整个事务回滚到事务的初始状态。(10)演示互参照问题及其解决方法。要建立教师授课和课程指定教师听课关系的

两张表,规定一个教师可以授多门课,但是每个课程只能指定一个教师去听课,所以要为

两张表建立相互之间的参照关系。

4. 实验步骤

以系统管理员或sa账号登录查询分析器,在查询分析器窗体中输入如下命令,运行

并观察结果。(1)在查询分析器中输入如下SQL语句(见2_2_1.sql):

犝犛犈犛犮犺狅狅犾犻狀狊犲狉狋犛狋狌_犝狀犻狅狀狏犪犾狌犲狊('10001','李勇','0',24,'犈犈')

狊犲犾犲犮狋犳狉狅犿犛狋狌_犝狀犻狅狀;

犮狉犲犪狋犲狋犪犫犾犲犆狅狌狉狊犲(

 犮狀狅犮犺犪狉(4)犖犗犜犖犝犔犔犝犖犐犙犝犈,

 犮狀犪犿犲狏犪狉犮犺犪狉(50)犖犗犜犖犝犔犔,

 犮狆狅犻狀狋狊犻狀狋,

 犮狅狀狊狋狉犪犻狀狋犘犓狆狉犻犿犪狉狔犽犲狔(犮狀狅));

犻狀狊犲狉狋犆狅狌狉狊犲狏犪犾狌犲狊('0001','犆狅犿狆狌狋犲狉犖犲狋狑狅狉犽狊',2);

犻狀狊犲狉狋犆狅狌狉狊犲狏犪犾狌犲狊('0002','犇犪狋犪犫狊犪犲',3);

注意:为演示参照完整性,预先建立表和数据。结果如图2.8所示。

图2.8 令cno为主键

(2)在查询分析器中输入如下SQL语句(见2_2_2.sql):

Page 72: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

65   第2章 数据库的完整性控制

犝犛犈犛犮犺狅狅犾犮狉犲犪狋犲狋犪犫犾犲犛犆(

狊狀狅犆犎犃犚(5)犚犈犉犈犚犈犖犆犈犛犛狋狌_犝狀犻狅狀(狊狀狅)狅狀犱犲犾犲狋犲犮犪狊犮犪犱犲,

犮狀狅犆犎犃犚(4)犚犈犉犈犚犈犖犆犈犛犆狅狌狉狊犲(犮狀狅)狅狀犱犲犾犲狋犲犮犪狊犮犪犱犲,

犵狉犪犱犲犐犖犜,

犆犗犖犛犜犚犃犐犖犜犘犓_犛犆犘犚犐犕犃犚犢犓犈犢(狊狀狅,犮狀狅));

犻狀狊犲狉狋犻狀狋狅狊犮狏犪犾狌犲狊('95002','0001',2);

犻狀狊犲狉狋犻狀狋狅狊犮狏犪犾狌犲狊('95002','0002',2);

犻狀狊犲狉狋犻狀狋狅狊犮狏犪犾狌犲狊('10001','0001',2);

犻狀狊犲狉狋犻狀狋狅狊犮狏犪犾狌犲狊('10001','0002',2);

狊犲犾犲犮狋犳狉狅犿犛犆;

结果如图2.9所示。(3)在查询分析器中输入如下SQL语句(见2_2_3.sql):

犝犛犈犛犮犺狅狅犾犻狀狊犲狉狋犻狀狋狅狊犮狏犪犾狌犲狊('99','101',2);

结果如图2.10所示。

图2.9 令(sno,cno)为主键 图2.10 违反参照完整性的插入数据

分析:违反了参照完整性,表stu_union中没有99的sno。(4)在查询分析器中输入如下SQL语句(见2_2_4.sql):

犝犛犈犛犮犺狅狅犾

犱犲犾犲狋犲犳狉狅犿犛狋狌_犝狀犻狅狀狑犺犲狉犲狊狀狅='10001';

狊犲犾犲犮狋犳狉狅犿犛犆;

结果如图2.11所示。

分析:由于ondeletecascade的连带删除作用,当表students中删除某个学 号,SC中对应这个学号为外键的所有记录都要被删除。

(5)在查询分析器中输入如下SQL语句(见2_2_5.sql):

犝犛犈犛犮犺狅狅犾

犱犲犾犲狋犲犳狉狅犿犆狅狌狉狊犲狑犺犲狉犲犮狀狅='0002';

狊犲犾犲犮狋犳狉狅犿犛犆;

结果如图2.12所示。

分析:由于ondeletecascade的连带删除作用,当Course中删除某个学号,SC中对

应这个学号为外键的所有记录都要被删除。

Page 73: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

66   数据库系统实验指导教程

图2.11 级联删除Stu_Union表中数据 图2.12 级联删除Course表中数据

(6)在查询分析器中输入如下SQL语句(见2_2_6.sql):

犝犛犈犛犮犺狅狅犾犮狉犲犪狋犲狋犪犫犾犲犛狋狌_犆犪狉犱(

犮犪狉犱_犻犱犮犺犪狉(14),

狊狋狌_犻犱犮犺犪狉(10)狉犲犳犲狉犲狀犮犲狊狊狋狌犱犲狀狋狊(狊犻犱)狅狀犱犲犾犲狋犲犮犪狊犮犪犱犲,

狉犲犿犪犻狀犲犱_犿狅狀犲狔犱犲犮犻犿犪犾(10,2),

犮狅狀狊狋狉犪犻狀狋犘犓_狊狋狌_犮犪狉犱犘狉犻犿犪狉狔犽犲狔(犮犪狉犱_犻犱))

犻狀狊犲狉狋犻狀狋狅犛狋狌_犆犪狉犱狏犪犾狌犲狊('05212567','800001216',100.25);

犻狀狊犲狉狋犻狀狋狅犛狋狌_犆犪狉犱狏犪犾狌犲狊('05212222','800005753',200.50);

狊犲犾犲犮狋犳狉狅犿犛狋狌_犮犪狉犱;

结果如图2.13所示。

图2.13 建立Stu_Card表

(7)在查询分析器中输入如下SQL语句(见2_2_7.sql):

犝犛犈犛犮犺狅狅犾犮狉犲犪狋犲狋犪犫犾犲犐犆犅犆_犆犪狉犱(

犫犪狀犽_犻犱犮犺犪狉(20),

狊狋狌_犮犪狉犱_犻犱犮犺犪狉(14)狉犲犳犲狉犲狀犮犲狊狊狋狌_犮犪狉犱(犮犪狉犱_犻犱)狅狀犱犲犾犲狋犲犮犪狊犮犪犱犲,

狉犲狊狋狅狉犲犱_犿狅狀犲狔犱犲犮犻犿犪犾(10,2),

犮狅狀狊狋狉犪犻狀狋犘犓_犐犮犫犮_犮犪狉犱犘狉犻犿犪狉狔犽犲狔(犫犪狀犽_犻犱))

犻狀狊犲狉狋犻狀狋狅犐犆犅犆_犆犪狉犱狏犪犾狌犲狊('9558844022312','05212567',15000.1);

犻狀狊犲狉狋犻狀狋狅犐犆犅犆_犆犪狉犱狏犪犾狌犲狊('9558844023645','05212222',50000.3);

狊犲犾犲犮狋犳狉狅犿犐犆犅犆_犆犪狉犱;

结果如图2.14所示。

图2.14 建立ICBC_Card表

(8)在查询分析器中输入如下SQL语句(见2_2_8.sql):

犝犛犈犛犮犺狅狅犾

Page 74: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

67   第2章 数据库的完整性控制

犪犾狋犲狉狋犪犫犾犲犮犺狅犻犮犲狊犱狉狅狆[犉犓_犆犎犗犐犆犈犛_犛犜犝犇犈犖犜犛];

犪犾狋犲狉狋犪犫犾犲犮犺狅犻犮犲狊犪犱犱犆犗犖犛犜犚犃犐犖犜[犉犓_犆犎犗犐犆犈犛_犛犜犝犇犈犖犜犛]犉犗犚犈犐犌犖犓犈犢

  (

    [狊犻犱]

  )犚犈犉犈犚犈犖犆犈犛[犱犫狅].[犛犜犝犇犈犖犜犛](

    [狊犻犱]

  )狅狀犱犲犾犲狋犲犮犪狊犮犪犱犲;

犱犲犾犲狋犲犳狉狅犿狊狋狌犱犲狀狋狊狑犺犲狉犲狊犻犱='800001216';

狊犲犾犲犮狋犳狉狅犿狊狋狌_犮犪狉犱;

狊犲犾犲犮狋犳狉狅犿犻犮犫犮_犮犪狉犱;

结果如图2.15所示。

图2.15 多重级联删除

分析:由于数据库中原有表choices使用了外键关联students表,采用了noaction,其外键又限定非空,所以直接在students中删除数据会出错。要演示多重级联删除必须

去除原有约束,并将其外键设置为级联删除。(9)在查询分析器中输入如下SQL语句(见2_2_9.sql):

犪犾狋犲狉狋犪犫犾犲犐犆犅犆_犆犪狉犱犱狉狅狆犮狅狀狊狋狉犪犻狀狋犉犓_犐犆犅犆_犆犪狉犱_狊狋狌_犮_6犮190犈犅犅;

犪犾狋犲狉狋犪犫犾犲犐犆犅犆_犆犪狉犱犪犱犱犮狅狀狊狋狉犪犻狀狋犉犓_犐犆犅犆_犆犪狉犱犳狅狉犲犻犵狀犽犲狔(狊狋狌_犮犪狉犱_犻犱)

狉犲犳犲狉犲狀犮犲狊犛狋狌_犮犪狉犱(犮犪狉犱_犻犱)狅狀犱犲犾犲狋犲狀狅犪犮狋犻狅狀;

注意:FK_ICBC_Card_stu_c_6c190EBB是原有外键的约束名称,可通过查询分析器

中的对象浏览器在相应表的约束中查到。结果如图2.16所示。在查询分析器中输入如下SQL语句(见2_2_10.sql):

犅犲犵犻狀犜狉犪狀狊犪犮狋犻狅狀犱犲犾犱犲犾犲狋犲犳狉狅犿狊狋狌犱犲狀狋狊狑犺犲狉犲狊犻犱='8000057';

狊犲犾犲犮狋犳狉狅犿狊狋狌_犮犪狉犱;

狊犲犾犲犮狋犳狉狅犿犻犮犫犮_犮犪狉犱;

犆狅犿犿犻狋犜狉犪狀狊犪犮狋犻狅狀犱犲犾

结果如图2.17所示。

Page 75: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

68   数据库系统实验指导教程

图 2.16 修改ICBC_Card表的外键属性   图2.17 多重级联删除失败

分析:事务del由于ICBC_Card表中的外键属性是ondeletenoaction所以多重级

联删除到了ICBC_Card无法执行,于是整个事务回滚。在查询分析器中输入如下SQL语句(见2_2_11.sql):

犝犛犈犛犮犺狅狅犾狊犲犾犲犮狋犳狉狅犿狊狋狌_犮犪狉犱;

狊犲犾犲犮狋犳狉狅犿犻犮犫犮_犮犪狉犱;

结果如图2.18所示。

图2.18 两张表建立相互之间的参照关系

分析:整个事务回滚,两个表的数据都没有被删除。(10)在查询分析器中输入如下SQL语句(见2_2_12.sql):

犝犛犈犛犮犺狅狅犾犮狉犲犪狋犲狋犪犫犾犲犾犻狊狋犲狀_犮狅狌狉狊犲(

  狋犲犪犮犺犲狉_犻犱犮犺犪狉(6),狋狀犪犿犲狏犪狉犮犺犪狉(20),犮狅狌狉狊犲_犻犱犮犺犪狉(4)

  犮狅狀狊狋狉犪犻狀狋犘犓_犾犻狊狋犲狀_犮狅狌狉狊犲狆狉犻犿犪狉狔犽犲狔(狋犲犪犮犺犲狉_犻犱)

  犮狅狀狊狋狉犪犻狀狋犉犓_犾犻狊狋犲狀_犮狅狌狉狊犲犳狅狉犲犻犵狀犽犲狔(犮狅狌狉狊犲_犻犱)

狉犲犳犲狉犲狀犮犲狊狋犲犪犮犺_犮狅狌狉狊犲(犮狅狌狉狊犲_犻犱))

犮狉犲犪狋犲狋犪犫犾犲狋犲犪犮犺_犮狅狌狉狊犲(

  犮狅狌狉狊犲_犻犱犮犺犪狉(4),犮狀犪犿犲狏犪狉犮犺犪狉(30),狋犲犪犮犺犲狉_犻犱犮犺犪狉(6)

  犮狅狀狊狋狉犪犻狀狋犘犓_狋犲犪犮犺_犮狅狌狉狊犲狆狉犻犿犪狉狔犽犲狔(犮狅狌狉狊犲_犻犱)

  犮狅狀狊狋狉犪犻狀狋犉犓_狋犲犪犮犺_犮狅狌狉狊犲犳狅狉犲犻犵狀犽犲狔(狋犲犪犮犺犲狉_犻犱)

狉犲犳犲狉犲狀犮犲狊犾犻狊狋犲狀_犮狅狌狉狊犲(狋犲犪犮犺犲狉_犻犱))

结果如图2.19所示。

图2.19 语句执行结果的错误信息

Page 76: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

69   第2章 数据库的完整性控制

解决方法如下:先定义listen_course表,但是不定义外键属性,再定义完整的teach_

course表,用altertable的命令定义listen_course表的外键属性。在查询分析器中输入如下SQL语句(见2_2_13.sql):

犝犛犈犛犮犺狅狅犾

犮狉犲犪狋犲狋犪犫犾犲犾犻狊狋犲狀_犮狅狌狉狊犲(

  狋犲犪犮犺犲狉_犻犱犮犺犪狉(6),狋狀犪犿犲狏犪狉犮犺犪狉(20),犮狅狌狉狊犲_犻犱犮犺犪狉(4)

  犮狅狀狊狋狉犪犻狀狋犘犓_犾犻狊狋犲狀_犮狅狌狉狊犲狆狉犻犿犪狉狔犽犲狔(狋犲犪犮犺犲狉_犻犱)

结果如图2.20所示。

图2.20 语句执行结果的正确提示

在查询分析器中输入如下SQL语句(见2_2_14.sql):

犝犛犈犛犮犺狅狅犾

犮狉犲犪狋犲狋犪犫犾犲狋犲犪犮犺_犮狅狌狉狊犲(

  犮狅狌狉狊犲_犻犱犮犺犪狉(4),犮狀犪犿犲狏犪狉犮犺犪狉(30),狋犲犪犮犺犲狉_犻犱犮犺犪狉(6)

  犮狅狀狊狋狉犪犻狀狋犘犓_狋犲犪犮犺_犮狅狌狉狊犲狆狉犻犿犪狉狔犽犲狔(犮狅狌狉狊犲_犻犱)

  犮狅狀狊狋狉犪犻狀狋犉犓_狋犲犪犮犺_犮狅狌狉狊犲犳狅狉犲犻犵狀犽犲狔(狋犲犪犮犺犲狉_犻犱)

狉犲犳犲狉犲狀犮犲狊犾犻狊狋犲狀_犮狅狌狉狊犲(狋犲犪犮犺犲狉_犻犱)

犪犾狋犲狉狋犪犫犾犲犾犻狊狋犲狀_犮狅狌狉狊犲

犪犱犱犮狅狀狊狋狉犪犻狀狋犉犓_犾犻狊狋犲狀_犮狅狌狉狊犲犳狅狉犲犻犵狀犽犲狔(犮狅狌狉狊犲_犻犱)

狉犲犳犲狉犲狀犮犲狊狋犲犪犮犺_犮狅狌狉狊犲(犮狅狌狉狊犲_犻犱);

结果如图2.20所示。

至此,互参照定义完成。

5. 习题

(1)用altertable语句将SC表中的ondeletecascade改为ondeleterestrict。重新

插入SC的数据。重复本实验的实验步骤中(4)、(5),观察结果,分析原因。

(2)使用altertable语句将SC表中的ondeletecascade改为ondeletesetNULL。

重新插入SC的数据。重复本实验的实验步骤中(4)、(5),观察结果,分析原因。

(3)创建一个班里的学生互助表,规定:包括学生编号,学生姓名,学生的帮助对象,

每个学生有且仅有一个帮助对象,帮助对象也必须是班里的学生。

(4)学校学生会的每个部门都有一个部长,每个部长领导多个部员,每个部只有一个

部员有监察评测部长的权力。请给出体现这两种关系(即领导和评测)的两张互参照的表

的定义。

Page 77: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

70   数据库系统实验指导教程

实验2.3 用户自定义完整性

1. 实验目的

  学习用户自定义约束,并实践用户自定义完整性,利用SQL查询分析器用短语NOTNULL、UNIQUE、CHECK保证用户定义完整性。

2. 原理解析

1)用户自定义完整性(UserdefinedIntegrity)

不同的关系数据库系统根据其应用环境的不同,往往需要一些特殊的约束条件。用

户定义的完整性即是针对某个特定关系数据库的约束条件,它反映某一具体应用所涉及

的数据必须满足的语义要求。它主要包括:(1)域完整性 (DomainIntegrity)。

① 域完整性是指数据库表中的列必须满足某种特定的数据类型或约束。域约束是

最常见的用户自定义完整性约束,当有新数据插入到数据库中,系统可以按照定义进行关

系属性取值是否正确的检测。其中,约束又包括取值范围精度等规定。表中的CHECKFOREIGNKEY约束和DEFAULTNOTNULL定义都属于域完整性的范畴。

② 现在的RDBMS中,一般都有域完整性检查功能。SQLServer提供了定义和检验

这类完整性的机制,以便用统一的系统方法来处理它们。而不是用应用程序来承担这一

功能。其他的完整性类型都支持用户定义的完整性。

③ 一个属性能否取空值一般由语义决定,也是域约束的内容之一。(2)其他用户定义完整性。

不同的关系数据库系统根据其应用环境的不同,往往还需要 一 些 特 殊 的 约 束 条 件。

其他类型的用户自定义的完整性即是针对某个特定关系数据库的约束条件,它反映某一

具体应用所涉及的数据必须满足的语义要求。

2)SQL中的约束机制

约束主要包括如下两种。(1)静态约束:对静态对象的约束是反映数据库状态合理性的约束,如实体完整性。(2)动态约束:对动态对象的约束是反映数据库状态变迁的约束,如触发器。

SQL中用于属性约束方面的有NotNULL、Check等子句;而用于全局约束方面的

有CreateAssertion、CreateRule等语句。

例如:

约束规则A1:化学系学生不能选修低于2学分的课程。

犆狉犲犪狋犲犃狊狊犲狉狋犻狅狀犃1犆犺犲犮犽(犖狅狋犈狓犻狊狋狊(犛犲犾犲犮狋犉狉狅犿犛犆

犠犺犲狉犲犛#犻狀(犛犲犾犲犮狋犛#犉狉狅犿犛

犠犺犲狉犲犱犲狆狋="化学")

Page 78: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

71   第2章 数据库的完整性控制

犃狀犱犆#犻狀(犛犲犾犲犮狋犆#犉狉狅犿犆犠犺犲狉犲犆犘<2)

))

3)一般的规则组成

规则一般是由规则标识(可默认)以及规则语义组成。规则语义由约束作用的数据对

象、约束语义(如断言 Assertion)、触发条件以及违反规则时的响应动作构成。例如:规则1:公司职工的底薪是3000元。受约束的对象:“职工”关系的Salary属性。约束语义:Title=职工时,Salary>=3000。触发条件:更新职工元组时。违反响应:拒绝执行更新操作。

4)规则的一般分类

规则的一般分类如表2.2所示。

表2.2 规则的一般分类

类型 属 性 级 元 组 级 关 系 级

静态 类型、格式、值域、空值元组 的 各 个 属 性 之 间 的 取

值限制

实体、引 用 统 计 完 整 性 函 数

依赖

动态 属性/值改变    元组 值 修 改 时 各 属 性 间 的

约束关系变化的前后一致性

例如:静态规则约束:“日期”的格式为YYMMDD;“成绩”的值域为0~100;“学号”不

能取空值;“职称”为教授时,“工资”值应大于3000;经理的工资不超过职工的平均工资

10倍(统计)。动态规则约束:更新时“工资”的新值应该超过旧值;事务的一致性,转账

前后余额的和保持不变。

5)自定义数据类型和规则

(1)如果多个列使用同一类型的约束,或在一些特殊的情况下,通过自定义的数据类

型和规则就为数据库设计提供了更高层次的抽象,如工资数据类型肯定比smallmoney能

建立更多的特征及更容易被理解。例如,某公司要计算加拿大元、美元、欧元、日元的数量,则DBA可 以 建 立 如 下 数 据

类型:

犲狓犲犮狊狆_犪犱犱狋狔狆犲犮犪狀犪犱犻犪狀_犱狅犾犾犪狉'犱犲犮犻犿犪犾(11,2)'

犲狓犲犮狊狆_犪犱犱狋狔狆犲犝犛_犱狅犾犾犪狉'犱犲犮犻犿犪犾(11,2)'

犲狓犲犮狊狆_犪犱犱狋狔狆犲犈狌狉狅'犱犲犮犻犿犪犾(11,2)'

犲狓犲犮狊狆_犪犱犱狋狔狆犲犑犪狆犪狀犲狊犲_狔犲狀'犱犲犮犻犿犪犾(11,2)'

将相应列分别指定数据类型后由于DBMS禁止在两种数据类型之间进行没有定义

Page 79: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

72   数据库系统实验指导教程

的操作,则下面的操作会禁止:

狋狅狋犪犾=犆犪狀犪_犃犮犮狅狌狀狋+犝犛_犃犮犮狅狌狀狋

(2)规则的创建(当约束条件要为多列使用时)。步骤一:

犆犚犈犃犜犈犚犝犔犈狉狌犾犲犃犛犮狅狀犱犻狋犻狅狀_犲狓狆狉犲狊狊犻狅狀

步骤二:

狊狆_犫犻狀犱狉狌犾犲[@狉狌犾犲狀犪犿犲= ]'狉狌犾犲',[@狅犫犼狀犪犿犲= ]'狅犫犼犲犮狋_狀犪犿犲'

规则一般绑定到某一列,或者是用户自己定义的数据类型,例如:

犈犡犈犆狊狆_犫犻狀犱狉狌犾犲'狋狅犱犪狔',犲犿狆犾狅狔犲犲狊.[犺犻狉犲犱犪狋犲]'

犈犡犈犆狊狆_犫犻狀犱狉狌犾犲'狉狌犾犲_狊狊狀','狊狊狀'

6)Check约束

(1)CHECK约束是对列或列的组合的取值限制,它采用SQL语句中 WHERE子句

相同的表达方式来表达,分为表一级和列一级的约束。(2)空值的问题。空值意味着检查约束的值是未知的,所以空值的出现不会违反检查约束的条件。(3)潜在的语义问题。

① 大多数的 DBMS不 会 检 查 约 束 和 默 认 值 定 义 的 语 义,所 以 要 注 意 语 义 冲 突,例如:

犲犿狆_狋狔狆犲犮犺犪狉(8)犇犲犳犪狌犾狋"狀犲狑"犮犺犲犮犽(犲犿狆_狋狔狆犲犻狀("狋犲犿狆","犳狌犾犾狋犻犿犲","犮狅狀狋狉犪犮狋"))

②check与check之间的冲突,例如:

犮犺犲犮犽(犲犿狆狀狅>10犪狀犱犲犿狆狀狅<9)

犮犺犲犮犽(犲犿狆狀狅>9)

犮犺犲犮犽(犲犿狆狀狅>=11)

③ 还可能存在的语义冲突:

定义了置空删除,但表中检查约束要求此列不能为空;

定义该列不能为空,检查约束要求此列为空。

7)rule约束的建立

规则可以是 WHERE子句中任何有效的表达式,并且可以包含诸如算术运算符、关

系运算符和谓词(如IN、LIKE、BETWEEN)之类的元素。规则不能引用列或其他数据

库对象。可以包含不引用数据库对象的内置函数。

condition_expression包含一个变量。每个局部变量的前面都有一个 @ 符号。该表

达式引用通过 UPDATE或INSERT语句输入的值。在创建规则时,可以使用任何名称

或符号表示值,但第一个字符必须是 @ 符号。例如:

Page 80: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

73   第2章 数据库的完整性控制

犆犚犈犃犜犈犚犝犔犈狉犪狀犵犲_狉狌犾犲犃犛@狉犪狀犵犲>= 1000犃犖犇@狉犪狀犵犲< 20000犆犚犈犃犜犈犚犝犔犈犾犻狊狋_狉狌犾犲犃犛@犾犻狊狋犐犖('1389','0736','0877')

犆犚犈犃犜犈犚犝犔犈狆犪狋狋犲狉狀_狉狌犾犲犃犛@狏犪犾狌犲犔犐犓犈'__%[09]'

注意:rule对于已经输入表中的数据不起作用。

8)rule的绑定以及松绑

创建规则后,规则仅仅只是一个存在于数据库中的对象,并未发生作用。需要将规则

与数据库表或用户自定义对象联系起来,才能达到创建规则的目的。联系的方法称为“绑定”,所谓“绑定”就是指定规则作用于哪个表的哪一列,或哪个用户自定义数据类型。表

的一列或一个用户自定义数据类型只能与一个规则相绑定,而一个规则可以绑定多对象。这正是规则的魅力所在。解除规则与对象的绑定称为“松绑”。

(1)存储过程Sp_bindrule绑定规则。存储过程Sp_bindrule可以绑定一个规则到表的一个列或一个用户自定义数据类型

上。其语法如下:

狊狆_犫犻狀犱狉狌犾犲[@狉狌犾犲狀犪犿犲=]'狉狌犾犲',[@狅犫犼狀犪犿犲=]'狅犫犼犲犮狋_狀犪犿犲'

[,'犳狌狋狌狉犲狅狀犾狔']

各参数说明如下:

① [@rulename=]'rule'指定规则名称。

② [@objname=]'object_name'指定规则绑定的对象。

③ 'futureonly'选项仅在绑定规则到用户自定义数据类型上时才 可 以 使 用。当 指 定

此选项时,仅以后使用此用户自定义数据类型的列会应用新规则,而当前已经使用此数据

类型的列则不受影响。(2)存储过程Sp_unbindrule解除规则的绑定。存储过 程Sp_unbindrule可 解 除 规 则 与 列 或 用 户 自 定 义 数 据 类 型 的 绑 定 其 语 法

如下:

狊狆_狌狀犫犻狀犱狉狌犾犲[@狅犫犼狀犪犿犲=]'狅犫犼犲犮狋_狀犪犿犲'

[,'犳狌狋狌狉犲狅狀犾狔']

其中'futureonly'选项同绑定时一样,仅用于用户自定义数据类型。它指定现有的用

此用户自定义数据类型定义的列仍然保持与此规则的绑定,如果不指定此项则所有由此

用户自定义数据类型定义的列也将随之解除与此规则的绑定。

3. 实验内容

(1)创建worker表,并自定义2个约束U1以及U2,其中U1规定Name字段唯一,

U2规定sage(级别)字段的上限是28。(2)在worker表中插入一条合法记录。(3)演示插入违反U2约束的例子,U2规定元组的sage属性的值必须<=28。(4)去除U2约束。

Page 81: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

74   数据库系统实验指导教程

(5)重新插入(3)中想要插入的数据,由于去除了U2约束,所以插入成功。(6)创建规则rule_sex,规定插入或更新的值只能是 M或F,并绑定到worker的sex

字段。(7)演示违反规则rule_sex的插入操作。

4. 实验步骤

以系统管理员或sa账号登录查询分析器,在查询分析器窗体下输入如下命令,运行

并观察结果。(1)在查询分析器中输入如下SQL语句(见2_3_1.sql):

犝犛犈犛犮犺狅狅犾犆狉犲犪狋犲犜犪犫犾犲犠狅狉犽犲狉(

犖狌犿犫犲狉犮犺犪狉(5),

犖犪犿犲犮犺犪狉(8)犮狅狀狊狋狉犪犻狀狋犝1狌狀犻狇狌犲,

犛犲狓犮犺犪狉(1),

犛犪犵犲犻狀狋犮狅狀狊狋狉犪犻狀狋犝2犮犺犲犮犽(犛犪犵犲<=28),

犇犲狆犪狉狋犿犲狀狋犮犺犪狉(20),

犮狅狀狊狋狉犪犻狀狋犘犓_犠狅狉犽犲狉犘狉犻犿犪狉狔犓犲狔(犖狌犿犫犲狉))

成功建立worker表,并自定义两个约束。(2)在查询分析器中输入如下SQL语句(见2_3_2.sql):

犝犛犈犛犮犺狅狅犾犐狀狊犲狉狋犐狀狋狅犠狅狉犽犲狉(犖狌犿犫犲狉,犖犪犿犲,犛犲狓,犛犪犵犲,犇犲狆犪狉狋犿犲狀狋)犞犪犾狌犲狊('00001','李勇','犕',14,'科技部')

犛犲犾犲犮狋犉狉狅犿犠狅狉犽犲狉

结果如图2.21所示。

图2.21 语句执行结果1

分析:合法插入。没有违反自定义约束。(3)在查询分析器中输入如下SQL语句(见2_3_3.sql):

犝犛犈犛犮犺狅狅犾

犐狀狊犲狉狋犐狀狋狅犠狅狉犽犲狉(犖狌犿犫犲狉,犖犪犿犲,犛犲狓,犛犪犵犲,犇犲狆犪狉狋犿犲狀狋)犞犪犾狌犲狊('00002','王勇','犕',38,'科技部')

犛犲犾犲犮狋犉狉狅犿犠狅狉犽犲狉

结果如图2.22所示。

图2.22 语句执行结果的错误信息1

Page 82: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

75   第2章 数据库的完整性控制

分析:违反了自定义约束U2,元组的sage属性的值必须<=28,插入失败。(4)在查询分析器中输入如下SQL语句(见2_3_4.sql):

犝犛犈犛犮犺狅狅犾犃犾狋犲狉犜犪犫犾犲狑狅狉犽犲狉犇狉狅狆犝2

分析:去除U2的自定义约束。(5)在查询分析器中输入如下SQL语句(见2_3_5.sql):

犝犛犈犛犮犺狅狅犾犐狀狊犲狉狋犐狀狋狅犠狅狉犽犲狉(犖狌犿犫犲狉,犖犪犿犲,犛犲狓,犛犪犵犲,犇犲狆犪狉狋犿犲狀狋)犞犪犾狌犲狊('00002','王勇','犕',38,'科技部')

狊犲犾犲犮狋犳狉狅犿狑狅狉犽犲狉

结果如图2.23所示。

图2.23 语句执行结果2

分析:由于去除了自定义约束U2,所以插入成功。(6)在查询分析器中输入如下SQL语句(见2_3_6.sql):

狌狊犲狊犮犺狅狅犾犵狅犮狉犲犪狋犲狉狌犾犲狉狌犾犲_狊犲狓犪狊@狏犪犾狌犲犻狀('犉','犕')

犵狅犲狓犲犮狊狆_犫犻狀犱狉狌犾犲狉狌犾犲_狊犲狓,'狑狅狉犽犲狉.[狊犲狓]';

结果如图2.24所示。分析:设置规则rule_sex并绑定到worker的sex字段上。(7)在查询分析器中输入如下SQL语句(见2_3_7.sql):

狌狊犲狊犮犺狅狅犾犻狀狊犲狉狋犻狀狋狅狑狅狉犽犲狉狏犪犾狌犲狊('00003','王浩','1','25','研发部');

结果如图2.25所示。

图2.24 语句执行结果3  图2.25 语句执行结果的错误信息2

分析:插入的数据违反了sex_rule规则,操作中止。

5. 习题

(1)加入约束U3,令sage的值>=0。

Page 83: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

76   数据库系统实验指导教程

(2)加入规则R2,确保插入的记录的sage值在1到100之间,并绑定到sage属性上。

实验2.4 触发器

1. 实验目的

  通过实验使学生加深对数据完整性的理解,学会创建和使用触发器。

2. 原理解析

1)触发器概述

触发器是SQL提供的一种维护数据完整性的工具。触发器过程是由程序员给定,例如一个和完整性控制动作有关的处理过程。当系统规定的触发条件发生时,给定的过程

被调用。触发条 件 是 多 种 多 样 的,例 如:进 入 或 退 出 程 序 的 某 层 结 构(如Block、Form等);查询、修改等操作发生之前或之后;某个按键动作;Trigger过程调用(相当于子程

序调用)。触发器是实施复杂完整性的特殊类型的存储类型。触发器不需要专门语句调用,当

对它所保护数据 进 行 修 改 时 自 动 激 活,以 防 止 对 数 据 进 行 不 正 确,未 授 权 或 不 一 致 的

修改。

2)触发器的类型及其具有的特殊表

一个触发器只适用于一个表,每个表最多只能有三个触发器,它们分别是INSERT、

UPDATE和DELETE触发器。触发器仅在实施数据完整性和处理业务规则时使用。每个触发器有两个特殊的表,即插入表(insertedtable)和删除表(deletedtable)。这

两个表是逻辑表,并且这两个表是由系统管理的,存储在内存中,不是存 储 在 数 据 库 中。因此不允许用户直接对其修改,并且这两个表的结构总是与被该触发器作用的表有相同

的表结构。

3)三种触发器的工作原理

(1)Insert触发器:先向inserted表中插入一个新行的副本,然后检查inserted表中

的新行是否有效,确定是否要阻止该插入操作。如果所插入的行中的值是有效的,则将该

行插入到触发器表。(2)Update触 发 器:先 将 原 始 数 据 行 移 到deleted表 中,然 后 将 一 个 新 行 插 入

inserted表中,最后计算deleted表和inserted表中的值以确定是否进行干预。(3)Delete触发器:将原始数据行移到deleted表中,计算deleted表中的值决定是否

进行干预,如果不进行,那么把数据行删除。

4)SQL中创建触发器的语法

创建触发器的语法为:

犆犚犈犃犜犈犜犚犐犌犌犈犚<触发器>犗犖<表名>[犠犐犜犎犈犖犆犚犢犘犜犐犗犖]

犉犗犚{[犇犈犔犈犜犈][,][犐犖犛犈犚犜][,][犝犘犇犃犜犈]}

Page 84: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

77   第2章 数据库的完整性控制

[犠犐犜犎犃犘犘犈犖犇][犖犗犜犉犗犚犚犈犘犔犐犆犃犜犐犗犖]

犃犛<犛犙犔语句组>

注意:创建触发器的语句一定要是SQL批处理语句中的第一句。

5)触发器和存储过程的区别

(1)是否附属于唯一的表。触发器附属于唯一的表,而存储过程不附属任何的表。(2)是否事件驱动。触发器由事件驱动,而存储过程由显式的指令调用。(3)是否有数量的限制。一般不允许在表级建立太多的触发器,对触发器的数目有要求,而存储过程没有这方

面的要求。

3. 实验内容

(1)为worker表建立触发器T1,当插入或是更新表中数据时,保证所操作的记录的

sage值大于0。(2)为worker表建立触发器T2,禁止删除编号为00001的CEO。(3)worker表中的人员的编号是唯一且不可改变的,创建触发器T3实现更新中编

号的不可改变性。(4)演示违反T1触发器的约束的插入操作。(5)演示违反T1触发器的约束的更新操作。(6)演示违反T2触发器的约束的插入操作。(7)演示违反T2触发器的约束的更新操作。

4. 实验步骤

(1)仍然使用自定义完整性实验中的worker表。为此表建立触发器T1,当插入或

是更新表中数据时,保证所操作的记录的sage值大于0。在查询分析器中输入如下SQL语句(见2_4_1.sql):

狌狊犲狊犮犺狅狅犾犵狅犮狉犲犪狋犲狋狉犻犵犵犲狉犜1狅狀狑狅狉犽犲狉犳狅狉犻狀狊犲狉狋,狌狆犱犪狋犲犪狊犻犳(狊犲犾犲犮狋狊犪犵犲犳狉狅犿犻狀狊犲狉狋犲犱)<1犫犲犵犻狀狆狉犻狀狋'犛犪犵犲犿狌狊狋犫犲犪犻狀狋犲犵犲狉犿狅狉犲狋犺犪狀狕犲狉狅!犜犪狀狊犪犮狋犻狅狀犳犪犻犾'

犚狅犾犾犫犪犮犽狋狉犪狀狊犪犮狋犻狅狀犈狀犱

(2)为worker表建立触发器T2,禁止删除编号为00001的CEO。

Page 85: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

78   数据库系统实验指导教程

在查询分析器中输入如下SQL语句(见2_4_2.sql):

狌狊犲狊犮犺狅狅犾犵狅犮狉犲犪狋犲狋狉犻犵犵犲狉犜2狅狀狑狅狉犽犲狉犳狅狉犱犲犾犲狋犲犪狊犻犳(狊犲犾犲犮狋狀狌犿犫犲狉犳狉狅犿犱犲犾犲狋犲犱)='00001'

犫犲犵犻狀狆狉犻狀狋'犎犲犻狊狋犺犲犆犈犗!犇犲犾犲狋犲犉犪犻犾!'

犚狅犾犾犫犪犮犽狋狉犪狀狊犪犮狋犻狅狀犈狀犱

(3)worker表中的人员的编号是唯一且不可改变的,创建触发器T3实现更新中编

号的不可改变性。在查询分析器中输入如下SQL语句(见2_4_3.sql):

狌狊犲狊犮犺狅狅犾犵狅犮狉犲犪狋犲狋狉犻犵犵犲狉犜3狅狀狑狅狉犽犲狉犳狅狉狌狆犱犪狋犲犪狊犻犳狌狆犱犪狋犲(狀狌犿犫犲狉)

犫犲犵犻狀狆狉犻狀狋'犈狏犲狉狔狀狌犿犫犲狉犮犪狀狀狅狋犫犲犮犺犪狀犵犲犱!'

犚狅犾犾犫犪犮犽犜狉犪狀狊犪犮狋犻狅狀犈狀犱

(4)在查询分析器中输入如下SQL语句(见2_4_4.sql):

狌狊犲狊犮犺狅狅犾犻狀狊犲狉狋犻狀狋狅狑狅狉犽犲狉狏犪犾狌犲狊('00003','李红','犉',10,'开发部')

结果如图2.26所示。

图2.26 结果信息1

分析:插入记录时违反T1触发器的约束,操作失败,回滚!

(5)在查询分析器中输入如下SQL语句(见2_4_5.sql):

狌狊犲狊犮犺狅狅犾

狌狆犱犪狋犲狑狅狉犽犲狉狊犲狋狊犪犵犲=7狑犺犲狉犲狀狌犿犫犲狉='00001'

结果如图2.27所示。

图2.27 结果信息2

Page 86: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

79   第2章 数据库的完整性控制

分析:更新记录时违反了触发器T1的约束,操作失败,回滚!

(6)在查询分析器中输入如下SQL语句(见2_4_6.sql):

狌狊犲狊犮犺狅狅犾犱犲犾犲狋犲犳狉狅犿狑狅狉犽犲狉狑犺犲狉犲狀犪犿犲='李勇'

结果如图2.28所示。分析:删除数据时违反触发器T2的约束,操作失败,回滚!

(7)在查询分析器中输入如下SQL语句(见2_4_7.sql):

狌狊犲狊犮犺狅狅犾狌狆犱犪狋犲狑狅狉犽犲狉狊犲狋狀狌犿犫犲狉='00007'狑犺犲狉犲狊犲狓='犉'

结果如图2.29所示。

图2.28 结果信息3 图2.29 结果信息4

分析:更新数据时违反触发器T3的约束,操作失败,回滚!

5. 习题

(1)建立一个在worker表上的触发器T4,要求插入记录的sage值必须比表中已记

录的最大sage值大。(2)建立一个在worker表上的触发器T5,要求当更新一个记录的时候,表中的记录

的sage值要比原记录的sage值大,因为一般工资级别只能升不能降。

习题答案

实验 2.1

  (1)USESCHOOLCreateTableClass(

 Class_idvarchar(4),namevarchar(10),Deparmentvarchar(20)

  constraintPK_ClassPrimarykey(Class_id))

(2)USESCHOOLBeginTransactionT3insertintoclassvalues('00001','01CSC','CS')

BeginTransactionT4insertintoclassvalues('00001','01CSC','CS')

CommitTransactionT4CommitTransactionT3

Page 87: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

80   数据库系统实验指导教程

结果由于T4中 的 插 入 违 法,T4失 败,而 且 整 个 T3事 务 回 滚,T3中 的 插 入 也 不

成功。

实验 2.2

(1)修改ondeletecascade为ondeleterestrict参看本实验的实验步骤中(7),其他

参照实验步骤中(4)、(5)。相应结果:数据库不允许删除students表以及course表中对应的元组。分析:由于ondeleterestrict的约束,数据库不允许任何引用关系存在对应元组时进

行删除操作。(2)修改ondeletecascade为ondeletesetNULL参看本实验的实验步骤中(7),其

他参照实验步骤中(4)、(5)。相应结果:数据库不允许删除students表以及course表中对应的元组。分析:约束ondeleteSetNULL是将要删除的对应元组的外键置空值,如果cno以

及sno不是SC表的主键,删除操作是可以完成的,但是由于主键不可以取空值,所以删

除操作不能进行。(3)

①createtablehelp(

 sidchar(8),snamevarchar(20),help_idchar(8)NOTNULL constraintPK_helpprimarykey(sid))

②altertablehelp  addconstraintFK_helpforeignkey(help_id)referenceshelp(help_id)(4)

①createtableleader(

 sidchar(9),snamevarchar(20),myleaderchar(9)

 constraintPK_leaderprimarykey(sid))

②createtablemonitor(

 sidchar(9),snamevarchar(20),mymonitorchar(9)

 constraintPK_monitorprimarykey(sid)

 constraintFK_monitorforeignkey(mymonitor)references

Page 88: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

81   第2章 数据库的完整性控制

Leader(sid)

 )

 altertableleader  addconstraintFK_leaderforeignkey(myleader)references    Monitor(sid)

实验 2.3

(1)USESchoolAltertableworkerAddconstraintU3check(sage>=0);

(2)USESchoolGoCreaterulerule_sageas@valuebetween1and100GoExecsp_bindrulerule_sage,'worker.[sage]';

实验 2.4

(1)USESchoolGoCreatetriggerT4onworkerForinsertAsIf(selectsagefrominserted)<=(selectmax(sage)fromworker)

Beginprint'Thesageofcouplemustbemorethantheexistedcouples'sage!'

RollbackTransactionEnd

(2)USESchoolGoCreatetriggerT5onworkerForupdateAsIf(selectsagefrominserted)<=(selectsagefromdeleted)

Beginprint'Thesageofnewcouplemustbemorethanthesageofoldcouple!'

RollbackTransactionEnd

Page 89: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

C H A P T E R 3

第3章 数据库的安全性控制

数据库的一大特点是数据可以共享,但数据共享必然带来数据库的安

全性问题,因为数据库系统中的数据共享不能是无条件的共享。数据库中

数据的共享是在DBMS统一的严格的控制之下的共享,即只允许有合法使

用权限的用户访问允许他存取的数据。因此数据库系统的安全保护措施

是否有效是数据库系统主要的性能指标之一。

数据库的安全性是指保护数据库以防止不合法的使用所造成的数 据

泄露、更改或破坏。对任何企业组织来说,数据的安全性最为 重 要。安 全

性主 要 是 指 允 许 那 些 具 有 相 应 的 数 据 访 问 权 限 的 用 户 能 够 登 录 到SQL

Server并访问数据以及对数据库对象实施各种权限范围内的操作,但是要

拒绝所有的非授权用户的非法操作。因此安全性管理与用户管理是密不

可分的。

SQLServer2000 提 供 了 内 置 的 安 全 性 和 数 据 保 护。SQLServer

2000 的 安 全 性 管 理 是 建 立 在 认 证 (authentication)和 访 问 许 可

(permission)两者机制上的。认证是指来确定登录SQLServer的用户的

登录账号和密码是否正确,以此来验证其是否具有连接SQLServer的权

限。但是,通过认证阶段并不代表能够访问SQLServer中的数据。用户只

有在获取访问数据库的权限之后,才能够对服务器上的数据库进行权限许

可下的各种操作,主要是针对数据库对象,如表、视图、存储过 程 等。这 种

用 户 访 问 数 据 库 权 限 的 设 置 是 通 过 用 户 账 号 来 实 现 的,同 时 在 SQL

Server中角色 作 为 用 户 组 的 代 替 物 大 大 地 简 化 了 安 全 性 管 理。所 以 在

SQLServer的安全模型中主要包括以下几部分:

SQLServer登录。

数据库用户。

权限。

角色。

Page 90: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

83   第3章 数据库的安全性控制

实验3.1 用户标识与鉴别

1. 实验目的

  本实验的目的是通过实验加深对数据安全性的理解,并掌握SQLServer中有关用户

登录认证以及管理的方法。

2. 原理解析

1)用户标识和鉴别

用户标识和鉴别是系统提供的最外层安全保护措施。其方法是由系统提供一定的方

式让用户标识自己的名字或身份。每次用户要求进入系统时,由系统进行核对,通过鉴定

后才提供机器使用权。对于获得上机权的用户若要使用数据库时数据库管理系统还要进行用户标识和鉴

定。用户标识和鉴定的方法有很多种,而且在一个系统中往往是多种方法并举,以获得更

强的安全性。常用的方法有:用一个用户名或者用户标识号来标明用户身份。系统内部记录着所有合法用户的标

识,系统用此鉴别此用户是否是合法用户,若是,则可以讲入下一步的核实;若不是,则不

能使用系统。用户标识和鉴定可以重复多次来增强数据库安全性。主要的方法有:

口令(Password):为了进一步核实用户,系统常常要求用户输入口令。为保密起

见,用户在终端上输入的口令不显示在屏幕上。系统核对口令以鉴别用户身份。口令虽然简单易行,但容易被人窃取。

计算过程或者函数:系统提供一个随机数,用户根据自己预先约定的计算过程或

者函数进行计算。系统根据用户计算结果是否正确鉴定用户身份。

2)SQLServer的登录认证

MSSQLServer能在两种安全模式下运行:

Windows认证模式。

混合模式。(1)Windows认证模式。

SQLServer数据库系统通常运行在 WindowsNT服务器平台或基于 WindowsNT构

架的 Windows2000上,而 WindowsNT作为网络操作系统,本身就具备管理登录。验证用

户合法性的能力。所以 Windows认证模式正是利用这一用户安全性和账号管理的机制,允许SQLServer也 可 以 使 用 WindowsNT 的 用 户 名 和 口 令。在 该 模 式 下 用 户 只 要 通 过

Windows的认证就可连接到SQLServer,而SQLServer本身也没有必要管理一套登录数据。

Windows认证模式比起SQLServer认证模式来有许多优点,原因在于 Windows认

证模式集成了 WindowsNT或 Windows2000的安全系统,并且 WindowsNT安全管理

具有众多特征,如安全合法性、口令加密、对密码最小长度进行限制等。所以当用户试图

登录到SQLServer时,它从 WindowsNT或 Windows2000的网络安全属性中获取登录

用户的账号与密码,并使用 WindowsNT或 Windows2000验证账号和密码的机制来检

Page 91: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

84   数据库系统实验指导教程

验登录的合法性,从而提高了SQLServer的安全性。在 WindowsNT中使用了用户组,所以当使用 Windows认证时,一般总是把用户归

入一定的 WindowsNT用户组,以便当在SQLServer中对 WindowsNT用户组进行数

据库访问权限设置时,能够把这种权限设置传递给单一用户,而且当新增加一个登录用户

时,也总把它归入某一 WindowsNT用户组,这种方法可以使用户更为方便地加入到系

统中,并消除了逐一为每一个用户进行数据库访问权限设置而带来的不必要的工作量。(2)混合认证模式。在混合认证模式下,Windows认证和SQLServer认证这两种认证模式都是可用的。

WindowsNT的用户既可以使用 WindowsNT认证,也可以使用SQLServer认证。(3)SQLServer认证模式。在该认证模式下,用户在连接SQLServer时必须提供登录名和登录密码,这些登录信

息存储在系统表syslogins中,与 WindowsNT的登录账号无关。SQLServer自己执行认证

处理,如果输入的登录信息与系统表syslogins中的某条记录相匹配,则表明登录成功。

3)数据库用户

数据库用户用来指出哪一个人可以访问哪一个数据库。在一个数据库中,用户ID唯一

标识一个用户,用户对数据的访问权限以及对数据库对象的所有关系都是通过用户账号来

控制的。用户账号总是基于数据库的,即两个不同数据库中可以有两个相同的用户账号。在数据库中,用户账号与登录账号是两个不同的概念。一个合法的登录账号只表明

该账号通过了 WindowsNT认证或SQLServer认证,但不能表明其可以对数据库数据

和数据对象进行某种或某些操作,所以一个登录账号总是与一个或多个数据库用户账号

(这些账号必须分别存在相异的数据库中)相对应,这样才可以访问数据库。例如,登录账

号sa自动与每一个数据库用户dbo相关联。

4)利用Transact_SQL管理SQLServer登录以及管理新数据库用户

(1)使用Transact_SQL管理SQLServer登录。

①sp_addlogin。创建新的使用SQLServer认证模式的登录账号,其语法格式为:

狊狆_犪犱犱犾狅犵犻狀[@犾狅犵犻狀犪犿犲=]'犾狅犵犻狀'

[,[@狆犪狊狊狑犱=]'狆犪狊狊狑狅狉犱'][,[@犱犲犳犱犫=]'犱犪狋犪犫犪狊犲'][,[@犱犲犳犾犪狀犵狌犪犵犲=]'犾犪狀犵狌犪犵犲'][,[@狊犻犱=]'狊犻犱'][,[@犲狀犮狉狔狆狋狅狆狋=]'犲狀犮狉狔狆狋犻狅狀_狅狆狋犻狅狀']

其中,

@loginame:登录名。

@passwd:登录密码。

@defdb:登录时默认数据库。

@deflanguage:登录时默认语言。

@sid:安全标识码。

@encryptopt:将密码存储到系统表时是否对其进行加密。@encryptopt这 个 参 数

Page 92: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

85   第3章 数据库的安全性控制

有如下三个选项。

NULL:表示对密码进行加密。

skip_encryption:表示对密码不加密。

skip_encryption_old:只在SQLServer升级时使用,表示旧版本已对密码加密。

②sp_droplogin。

SQLServer中删除该登录账号禁止其访问SQLServer,其语法格式为:

狊狆_犱狉狅狆犾狅犵犻狀[@犾狅犵犻狀犪犿犲=]'犾狅犵犻狀'

(2)使用Transact_SQL管理新数据库用户。除了guest用户外,其他用户必须与某一登录账号相匹配,所以,不仅要输入新创建

的新数据库用户名称,还要选择一个已经存在的登录账号。同理,当使用系统过程时,也

必须指出登录账号和用户名称。

①sp_grantdbaccess。系统过程sp_grantdbaccess就是被用来为SQLServer登录者或 WindowsNT用户

或用户组建立一个相匹配的数据用户账号。其语法格式为:

狊狆_犵狉犪狀狋犱犫犪犮犮犲狊狊[@犾狅犵犻狀犪犿犲=]'犾狅犵犻狀'

[[@狀犪犿犲_犻狀_犱犫=]'狀犪犿犲_犻狀_犱犫'[犗犝犜犘犝犜]]

其中,

@loginame:表示SQLServer登录账号或 WindowsNT用户或用户组。如果使用

的是 WindowsNT用户或用户组,那么必须给出 WindowsNT主机名称或 WindowsNT网络域名。登录账号或 WindowsNT用户或用户组必须存在。

@name_in_db:表示登录账号相匹配的数据库用户账号。该数据库用户账号并不存在

于当前数据库中,如果不给出该参数值,则SQLServer把登录名作为默认的缺省用户名称。

②sp_revokedbaccess。系统过程sp_revokedbaccess用来将数据库用户从当前数据库中删除,其相匹配的登

录者就无法使用该数据库。sp_revokedbaccess的语法格式为:

狊狆_狉犲狏狅犽犲犱犫犪犮犮犲狊狊[@狀犪犿犲_犻狀_犱犫=]'狀犪犿犲'

@name_in_db含义参看sp_granddbaccess语法格式。

3. 实验内容

(1)在SQLServer企业管理器中,设置SQLServer的安全认证模式。(2)在SQLServer中建立一个名为“李勇”的登录用户、数据库用户。(3)演示在SQLServer中取消“李勇”这个用户。

4. 实验步骤

(1)在SQLServer企 业 管 理 器 中 把 所 属 的SQL 服 务 器 设 置 为SQLServer和

WindowsNT混合安全认证模式。其操作如下:在企业管理器窗口中展开服务器组,用鼠标右击需要设置的SQL服务器,在弹出的

菜单中选择“属性”项,则出现SQLServer属性对话框,如图3.1所示。

Page 93: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

86   数据库系统实验指导教程

图3.1 SQLServer属性对话框

在SQLServer属性对话框中,选择“安全性”选项卡,在“身 份 验 证”一 栏 选 择“SQLServer和 Windows”单选按钮。

(2)在SQLServer企业管理器中为自己建立一个服务器用户、数据库用户。采用两

种方法:方法一,在企业管理器窗口中展开服务器组,展开服务器,用鼠标单击“安全性”文件

夹右侧的‘+’,用鼠标右击“登录”,在弹出的菜单中选择“新建登录”项,则出现新建登录

对话框,如图3.2所示。

图3.2 新建登录对话框

Page 94: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

87   第3章 数据库的安全性控制

在新建登录对话框中有常规,服务器角色和数据库访问三个选项卡进行设置:

① 在常规选项卡中,输入用户名(本例为李勇),选择SQLServer安全验证,输入用

户口令。

② 在服务器角色选项卡中,需要确定用户所属的服务器角色,在本例中采用默认值即可。

③ 在数据库访问选项卡中,需要指定此登录可以访问的数据库(本例选school数据

库)和“public”的数据库角色(在本例中采用默认值即可)。单击“确定”按钮,即完成了创建登录用户的工作。方法二,在SQLServer的查询分析器中建立登录账号。以系统管理员或sa用户登录进入查询分析器,在查询分析器中输入建立登录账号的

语句,然后执行就可。代码如下(见3_1_1.sql):

犲狓犲犮狊狆_犪犱犱犾狅犵犻狀'李勇','123456','狊犮犺狅狅犾','犈狀犵犾犻狊犺'

犵狅狌狊犲狊犮犺狅狅犾犵狅犲狓犲犮狊狆_犵狉犪狀狋犱犫犪犮犮犲狊狊'李勇'

结果如图3.3所示。(3)使用Transact_SQL撤销李勇这个登录账号。以系统管理员或sa用户登录进入查询分析器。在查询分析器中输入取消登录账号

语句(由于school数据库已经授权给李勇,所以要先执行取消其数据库权限的语句),然

后执行即可。代码如下(见3_1_2.sql):

狌狊犲狊犮犺狅狅犾犲狓犲犮狊狆_狉犲狏狅犽犲犱犫犪犮犮犲狊狊'李勇';

犲狓犲犮狊狆_犱狉狅狆犾狅犵犻狀'李勇';

结果如图3.4所示。

图3.3 结果信息1 图3.4 结果信息2

5. 习题

(1)在school数据库中创建账号“王二”,密码是123,并向他授予数据库访问权。(2)撤销“王二”这个账号。

实验3.2 自主存取控制

1. 实验目的

  通过实验加深对数据库存取控制机制的理解,通过自主存取控制进行权限管理,熟悉

SQLServer中的角色管理。

Page 95: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

88   数据库系统实验指导教程

2. 原理解析

1)存取控制机制

数据库安全性所关心的主要是DBMS的存取控制机制。数据库安全最重要的一点

就是确保只授权给有资格的用户访问数据库的权限,同时令所有未被授权的人员无法接

近数据,这主要通过数据库系统的存取控制机制实现。某个用户对某类数据具有何种操作权力是个政策问题而不是技术问题。数据库管理

系统的功能是保证这些决定的执行。为此DBMS必须具有以下功能:(1)把授权的决定告知系统,这是由SQL的GRANT和REVOKE语句来完成的。(2)把授权的结果存入数据字典。(3)当用户提出操作请求时,根据授权情况进行检查,以决定是否执行操作请求。存取控制机制主要包括两部分:(1)定义用户权限,并将用户权限登记到数据字典中。用户权限是指不同的用户对

于不同的数据对象允许执行的操作权限。系统必须提供适当的语言定义用户权限,这些

定义经过编译后存放在数据字典中,被称作安全规则或授权规则。用户权限是由两个要素组成的:数据对象和操作类型。定义一个用户的存取权限就

是要定义这个用户可以在哪些数据对象上进行哪些类型的操作。在数据库系统中,定义

存取权限称为授权(Authorization)。(2)合法权限检查,每当用户发出存取数据库的操作请求后(请求一般应包括操作类

型、操作对象和操作用户等信息),DBMS查找数据字典,根据安全规则进行 合 法 权 限 检

查,若用户的操作请求超出了定义的权限,系统将拒绝执行此操作。用户权限定义和合法权检查机制一起组成了DBMS的安全子系统。

2)自主存取控制(DiscretionaryAccessControl,DAC)(1)自主存取方法。相对于强制存取方法,其不同点体现在:

同一用户对于不同的数据对象有不同的存取权限。

不同的用户对同一对象也有不同的权限。

用户还可将其拥有的存取权限转授给其他用户。大型数据库管理系统几乎都支持自主存取控制,目前的SQL标准也对自主存取控制

提供支持,这主要通过SQL的GRANT语句和REVOKE语句来实现。(2)检查存取权限。对于获得上机权后又进一步发出存取数据库操作 的 用 户,DBMS查 找 数 据 字 典,根

据其存取权限对操作的合法性进行检查。若用户的操作请求超出了定义的权限,系统将

拒绝执行此操作。(3)授权粒度。授权粒度是指可以定义的数据对象的范围,它是衡量授权机制是否灵活的一个重要

指标。授权定义中数据对象的粒度越细,即可以定义的数据对象的范围越小,授权子系统

就越灵活。关系数据库中授权的数据对象粒度从大到小为数据库、表、属性列、元组。

Page 96: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

89   第3章 数据库的安全性控制

(4)自主存取控制的优缺点。优点:能够通过授权机制有效地控制其他用户对敏感数据的存取。缺点:可能存在数据的“无意泄露”,因为这种机制仅仅通过对数据的存取权限来进

行安全控制,而数据本身并无安全性标记。一种解决方法是对系统控制下的所有主客体

实施强制存取控制策略。

3)SQLServer中的权限管理

在SQLServer中包括两种类型的权限,即对象权限和语句权限。(1)对象权限。对象权限总是针对表、视图、存储过程而言,它决定了能对表、视图、存储过程执行哪

些操作(如UPDATE、DELETE、INSERT、EXECUTE)。如果用户想要对某一对象进行

操作,他必须具有相应的操作的权限。例如当用户要成功修改表中数据,那么前提条件是

他已经被授予表的UPDATE权限。不同类型的对象支持不同的针对它的操作,例如不能对表对象执行EXECUTE类型

的操作。对各种对象的可能操作如表3.1所示:

表3.1 各种对象所支持的操作表

对 象 可能的操作

表 SELECT、INSERT、UPDATE、DELETE、REFERENCE视图 SELECT、UPDATE、INSERT、DELETE存储过程 EXECUTE列 SELECT、UPDATE

(2)语句权限。语句权限主要指用户是否具有权限来执行某一语句,这些语句通常是一些具有管理

性的操作,如创建数据库、表、存储过程等。这种语句虽然仍包含有操作(如CREATE)的

对象,但这些对象在执行该 语 句 之 前 并 不 存 在 于 数 据 库 中(如 创 建 一 个 表,在CREATETABLE语句未成功执行前数据库中没有该表),这一类属于语句权限范畴。

(3)SQL中的权限管理。在SQLServer中使用GRANT、REVOKE和DENY三种命令来管理权限。

① GRANT:用来把权限授予某一用户,以允许该用户执行针对该对象的操 作(如

UPDATE、SELECT、DELETE、EXECUTE)或 允 许 其 运 行 某 些 语 句(如 CREATETABLE、CREATEDATABASE)。

GRANT语句的一般格式为:

犌犚犃犖犜<权限> [,<权限>]…[犗犖<对象类型><对象名>]

  犜犗<用户>[,<用户>]…[犠犐犜犎犌犚犃犖犜犗犘犜犐犗犖];

其语义是:将对指定操作对象的指定操作权限授予指定的用户。其中,对 属 性 列 和 视 图 的 操 作 权 限 有 查 询(SELECT)、插 入(INSERT)、修 改

(UPDATE)、删除(DELETE)以及这四种权限的总和(ALLMVILEGES)。

Page 97: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

90   数据库系统实验指导教程

对基本表的 操 作 权 限 有 查 询(SELECT)、插 入(INSERT)、修 改(UPDATE)、删 除

(DELETE)、修 改 表(ALTER)和 建 立 索 引(INDEX)以 及 这 六 种 权 限 的 总 和(ALLPRIVTLEGES)。

对数据库可以有建立表(CREATETAB)的权限,该 权 限 属 于DBA,可 由DBA授 予

普通用户,普通用户拥有此权限后可以建立基本表,基本表的属主(Owner)拥有对该表的

一切操作权限。接受权限的用户可以是一个或多个具体用户,也可以是PUBLIC,即全体用户。如果指定了 WITHGRANTOPTION子句,则获得某种权 限 的 用 户 还 可 以 把 这 种

权限再授予其他的用户。如果没有指定 WITHGRANTOPTION子 句,则 获 得 某 种 权

限的用户只能使用该权限,但不能传播该权限。

② REVOKE:取消用户对某一对象或语句的权限(这些权限是经过GRANT 语句

授予的),不 允 许 该 用 户 执 行 针 对 数 据 库 对 象 的 某 些 操 作(如 UPDATE、SELECT、

DELETE、EXECUTE),或 不 允 许 其 运 行 某 些 语 句(如 CREATETABLE、CREATEDATABASE)。

REVOKE语句的一般格式为:

犚犈犞犗犓犈<权限> [,<权限>]…[犗犖<对象类型> <对象名> ]

犉犚犗犕<用户>[,<用户>]…;

其中各种属性和grant相同。

③ DENY:用来禁止用户对某一对象或语句的权限,明确禁止其对某一用户对象执

行某些 操 作(如 UPDATE、SELECT、DELETE、EXECUTE)或 运 行 某 些 语 句(如

CREATETABLE、CREATEDATABASE)。

4)角色和SQLServer中的角色管理

(1)除了给个别用户授予权限,DBMS可能还提供以下的授权功能:

给一个角色制定权限,然后把角色赋予用户。

给用户制定内建的权限组。注意:在有的DBMS中,角色就是组,或者组就是角色。(2)SQLServer提供了两种数据库角色,类型预定义的数据库角色以及用户自定义

的数据库角色。

① 预定义数据库角色。预定义数据库角色是指这些角色所有具有的管理、访问数据库权限已被SQLServer预

先定义,并且SQLServer管理者不能对其所具有的权限进行任何修改。SQLServer中的每

一个数据库中都有一组预定义的数据库角色,在数据库中使用预定义的数据库角色,可以将

不同级别的数据库管理工作分给不同的角色,从而很容易实现工作权限的传递。在SQLServer中预定义的数据库角色如下所述,分别有以下几种:

db_owner:数据库的所有者,可以执行任何数据库管理工作,可以对数据库内的任何

对象进行任何操作,如删除、创建对象,将对象权限指定给其他用户。该角色包含下面各

角色的所有权限。

Page 98: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

91   第3章 数据库的安全性控制

db_accessadmin:可 增 加 或 删 除 WindowsNT认 证 模 式 下 WindowsNT 用 户 或

WindowsNT用户组登录者以及SQLServer用户。

db_datareader:能且仅能对数据库中任何表执行SELECT操作,从而读取所有表的信息。

db_datawriter:能对数据库中任何表执行INSERT、UPDATE、DELETE操作,但不

能进行SELECT操作。

db_addladmin:可以新建、删除、修改数据库中任何对象。

db_securityadmin:管理数据 库 内 权 限 的GRANT、DENY 和REVOKE,主 要 包 括

语句和对象权限,也包括对角色权限的管理。

db_backupoperator:可以备份数据库。

db_denydatareader:不能对数据库中任何表执行SELECT操作。

db_denydatawriter:不能对数据库中任何表执行UPDATE、DELETE和INSERT操作。

② 用户自定义角色。当DBA打算为某些数据库用户设置相同的权限,但是这些权限不等同于预定义的数据

库角色所具有的权限时,那么就可以定义新的数据库角色来满足这一要求,从而使这些用户

能够在数据库中实现某一特定功能。用户自定义的数据库角色具有以下几个优点:

SQLServer数据库角色可以包含 WindowsNT用户组或用户;

在同一数据库中用 户 可 以 具 有 多 个 不 同 的 自 定 义 角 色,这 种 角 色 的 组 合 是 自 由

的,而不仅仅是public与其他一种角色的结合;

角色可以进行嵌套,从而在数据库实现不同级别的安全性。

5)权限的授予和回收

DBMS中允许用户之间的权限相互授予,权限转授如图3.5所示。权限授予和回收有时候会使得用户的权限混淆不清,但必须牢记一点便可以理清关

系,那就是一个用户拥有权限的充分必要条件是在权限图中从根节点到该用户节点存在

一条路径。如图3.6所示,当DBA回收了 U3用户的权限后,U2用户不存在从DBA到

它的一条授权路径,那么DBMS会自动检查,U2和U3最终都不具有权限。

图3.5 权限转授 图3.6 权限授予和回收

图3.7 U3仍具有权限

一对用户可能企图通过相互授权来破坏回

收规则,所 以 要 求 授 权 图 中 所 有 边 都 是 某 条 从

DBA开始的路径的一部分,不要形成循环路径。当DBA回 收 了 U3的 权 限 时,由 于 DBA->U2->U3仍存在一条路径,于是 U3仍然具有

权限。如图3.7所示。

Page 99: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

92   数据库系统实验指导教程

3. 实验内容

(1)分别通过SQLServer企业管理器和SQL的数据控制功能,设置和管理数据操

作权限。对新建用户李勇授予School数据库中students表的select权限。

(2)通过SQLServer企业管理器,实现对SQLServer的用户和角色管理。具体是

创建一个数据库角色 OP_of_students,它 代 表 一 个 可 以 对students表 进 行 操 作 的 操 作

员,对角色的权限进行设置,并将用户“李勇”、“web”添加到这个角色中。该实验体现角

色应用灵活高效的特点。

4. 实验步骤

(1)在SQLServer中建立一个名为“李勇”的登录用户、School数据库的用户。参见

实验3.1的实验步骤中(2)。

(2)关闭SQLServer企业管理器,打开SQL查询分析器。选择SQLServer安全验

证,用户名为李勇,输入用户口令,连接 到SQLServer。在“查 询”窗 口 中 可 以 输 入SQL语句“SELECTFROMSTUDENTS”(见3_2_1.sql)。运行后,得到消息“拒绝了对对

象‘students’(数据库‘School’,所有者‘dbo’)的SELECT权限”。可见用户李勇没有对

学生表的SELECT权限。如图3.8所示。

图3.8 select语句和其查询结果

Page 100: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

93   第3章 数据库的安全性控制

(3)将School数据库的操作权限赋予数据库用户李勇,有如下两种方法。方法一,通过企业管理器。在企业管理器窗口中展开服务器组,展开服务器,用鼠标单击“数据库”文件夹右侧的

图3.9 数据库用户属性对话框

“+”,再用 鼠 标 单 击School数 据 库 文 件 夹 右

侧的“+”,用 鼠 标 右 击“用 户”。在 屏 幕 右 侧

的“用户”窗口中选择“李勇”项,用鼠标右击,

在弹出的 菜 单 中 选 择“属 性”项,则 出 现 数 据

库用户属性对话框,如图3.9所示。

单击图3.9中的权限按钮,则出现数据库

用户权限对话框,如图3.10所示。对 话 框 的

下面是有关数据库用户和角色所对应的权限

表。这些权限均以复选框的形式 表 示。复 选

框有三种状态:“√”为授权;“×”为废除权;

空为撤权。在表中可以对用户或角色的各 种

对象 操 作 权(SELECT,INSERT,UPDATE,

DELETE,EXEC和DRI)进行授予或撤销。

图3.10 数据库用户权限对话框

在图3.10中找到students表,授予SELECT权限,即让学生表与SELECT列交叉

的复选框为“√”即可。

方法二,通过SQL的数据控制功能。

对用户李勇授权,必须是数据库对象拥有者以上用户授予。可以通过以系统管理员

或sa用户登录进入查询分析器。在查询分析器中输入授权语句“GRANTSELECTONstudentsTO李勇;”,然后执行即可。代码如下(见3_2_2.sql):

Page 101: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

94   数据库系统实验指导教程

狌狊犲狊犮犺狅狅犾犵狉犪狀狋狊犲犾犲犮狋狅狀狊狋狌犱犲狀狋狊狋狅李勇;

(4)启动SQLServer企业管理器登录到指定的服务器,展开School数据库选中角

色图标。右击图标在弹出菜单中选择新建数据库角色选项,弹出“数据库角色属性-新建

角色”对话框。如图3.11所示。

图3.11 “数据库角色属性-新建角色”对话框

在名称一栏中输入OP_of_students,表示是对students表有权限的操作者,选择标

准角色,在添加一栏中添加进“李勇”、“web”(或是任意用户),单击“确定”按钮结束。如

图3.12所示。

图3.12 添加用户

Page 102: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

95   第3章 数据库的安全性控制

重复以上步骤,再次进入School数据库的角色目录,选 择OP_of_students角 色,右

击从 快 捷 菜 单 中 选 择 属 性,进 入 对 话 框,单 击 权 限,此 时 便 可 以 进 入 权 限 设 置。把

students表 上 的 SELECT、UPDATE、DELECT、INSERT 四 种 权 限 都 赋 予 OP_of_

students角色,如图3.13所示。

图3.13 赋予角色属性

5. 习题

(1)以SA登录查询分析器,输入下列代码并执行:

1行:犈犡犈犆狊狆_犪犱犱犾狅犵犻狀'李勇','123456';

2行:犝犛犈犛犮犺狅狅犾

3行:犈犡犈犆狊狆_犵狉犪狀狋犱犫犪犮犮犲狊狊'李勇','犺犪狆狆狔狉犪狋';

4行:犌犚犃犖犜狊犲犾犲犮狋,犻狀狊犲狉狋,狌狆犱犪狋犲犗犖狊狋狌犱犲狀狋狊犜犗狆狌犫犾犻犮;

5行:犌犚犃犖犜犃犔犔犗犖狊狋狌犱犲狀狋狊犜犗犺犪狆狆狔狉犪狋;

6行:犚犈犞犗犓犈狊犲犾犲犮狋犗犖狊狋狌犱犲狀狋狊犜犗犺犪狆狆狔狉犪狋;

7行:犇犈犖犢狌狆犱犪狋犲犗犖狊狋狌犱犲狀狋狊犜犗犺犪狆狆狔狉犪狋;

(2)回答下列问题:

第1行代码新建了一个名为李勇的登录账户,“123456”是什么?“李勇”这个登录账

户将映射为数据库用户名happyrat,为什么?将是哪个数据库的用户。

分别解释第4~7行代码的作用。

若以账户李勇登录服务器,能否对School数据库的表students进行select和update操作,为什么?

Page 103: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

96   数据库系统实验指导教程

实验3.3 视图机制在自主存取控制上的应用

1. 实验目的

  通过实验加深对数据安全性的理解,熟悉视图机制在自主存取控制上的应用。

2. 原理解析

1)视图机制

为了说明视图机制的优点,先回顾一下授权粒度的定义:授权粒度是指可以定义的

数据对象的范围,它是衡量授权机制是否灵活的一个重要指标。授权定义中数据对象的

粒度越细,即可以定义的数据对象的范围越小,授权子系统就越灵活。关系数据库中授权

的数据对象粒度从大到小为数据库、表、属性列、元组。直接使用授权机制所能达到的数据对象的粒度最小只能是属性列,为了使数据粒度

可以达到元组这一级,必须利用视图机制与授权机制配合使用。视图机制与授权机制配合使用,首先可以利用视图机制屏蔽掉一部分保密数据,然后

在视图上面再进一步定义存取权限,从而实现了水平子集和垂直子集上的安全,并且间接

实现了支持存取谓词的用户权限定义。例如,用户王平只能检索计算机系老师的信息。

① 先建立计算机系学生的视图CS_Teacher:

犆犚犈犃犜犈犞犐犈犠犆犛_犜犲犪犮犺犲狉犃犛犛犈犔犈犆犜犉犚犗犕犜犲犪犮犺犲狉犠犎犈犚犈犱犲狆狋='犆犛';

② 在视图上进一步定义存取权限:

犌犚犃犖犜犛犈犔犈犆犜犗犖犆犛_犛狋狌犱犲狀狋犜犗王平 ;

2)视图的权限问题

如果一个用户创建了一个对象(关系/视图/角色),则拥有对此基表的全部权限(包括

授予别人权限的权限)。创建视图不需要授权,但是如果用户对基表没有任何权限,系统

会拒绝创建视图。

3. 实验内容

(1)创建在选课表CHOICES上的视图CS_View,授权给计算机系的开计算科学这

门课程(课程号:10010)的数据库用户李勇,让他具有视图上的select权限。(2)对视图上的score属性列的update权限授予用户李勇,让他可以修改学生的成

绩,但是不能对学生的基本信息,如学号、选课号进行修改。

Page 104: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

97   第3章 数据库的安全性控制

4. 实验步骤

(1)在数据库School上创建用户“李勇”,具体操作参见实验3.1实验步骤中的(2)。(2)打开查询分析器,用sa登录数据库。在choices表上创建视图cs_view(选课课

程号10010),代码如下(见3_3_1.sql):

犝狊犲犛犮犺狅狅犾

犵狅

犮狉犲犪狋犲狏犻犲狑犮狊_狏犻犲狑犪狊

图3.14 命令执行成功    

狊犲犾犲犮狋犳狉狅犿犮犺狅犻犮犲狊

狑犺犲狉犲犮犻犱='10010'

结果如图3.14所示。(3)在视图cs_view上给用户李勇授予select的权限,代码如下(见3_3_2.sql):

犝狊犲犛犮犺狅狅犾

犵狅

犵狉犪狀狋狊犲犾犲犮狋狅狀犮狊_狏犻犲狑

狋狅李勇

结果如图3.14所示。(4)将视图cs_view上score列的权限赋予用户“李勇”,代码如下(见3_3_3.sql):

犝狊犲犛犮犺狅狅犾

犵狅

犵狉犪狀狋狌狆犱犪狋犲狅狀犱犫狅.[犮狊_狏犻犲狑]([狊犮狅狉犲])

狋狅李勇

结果如图3.14所示。(5)以用户李勇登录查询分析器,对cs_view进行查询操作,代码如下(见3_3_4.sql):

犝狊犲犛犮犺狅狅犾

犵狅

狊犲犾犲犮狋犳狉狅犿犮狊_狏犻犲狑

结果如图3.15所示。

图3.15 查询操作结果

Page 105: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

98   数据库系统实验指导教程

(6)对no为500024940的学生的成绩进行修改,改为90分,代码如下(见3_3_5.sql):

犝狊犲犛犮犺狅狅犾犵狅狌狆犱犪狋犲犮狊_狏犻犲狑狊犲狋狊犮狅狉犲=90

狑犺犲狉犲狀狅=500024940狊犲犾犲犮狋犳狉狅犿犮狊_狏犻犲狑

结果如图3.16所示。

图3.16 修改操作结果

5. 习题

(1)在数据库School上创建用户“王二”,具体操作参见实验3.1的实验步骤中(2)。在students表上创建视图grade2000,把年级为2000的学生元组放入视图。

(2)授予用户王二在视图grade2000的select权限。

习题答案

实验 3.1

  (1)以系统管理员或sa用户登录进入查询分析器,执行下面的代码:

犈狓犲犮狊狆_犪犱犱犾狅犵犻狀'王二 ','123 ','狊犮犺狅狅犾'

犌狅犝狊犲犛犮犺狅狅犾犌狅犈狓犲犮狊狆_犵狉犪狀狋犱犫犪犮犮犲狊狊'王二 '

(2)

犝狊犲犛犮犺狅狅犾犈狓犲犮狊狆_狉犲狏狅犽犲犪犮犮犲狊狊'王二';犈狓犲犮狊狆_犱狉狅狆犾狅犵犻狀'王二'

实验 3.2

“123456”是 新 增 用 户 的 登 录 密 码。“李 勇”登 录 账 户 将 映 射 为 数 据 库 用 户 名

happyrat,这是由第3行代码的存储过程决定的,将是school数据库的用户。

Page 106: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

99   第3章 数据库的安全性控制

第4行的 作 用 是 把students表 上 的select、insert、update三 种 权 限 授 予 全 体 用 户。第5行 的 作 用 是 把students表 的 所 有 权 限 授 予happyrat用 户。第6行 的 作 用 是 取 消

happyrat用户在students上的select权限。第7行是拒绝happyrat用户在students上

的update操作。若以账户李勇登录数据库,则可以对school数据库的表students进行select操作,但是

update操作 不 行,因 为update操 作 被deny了 的,而happyrat用 户 的select权 限 虽 然 被

revoke,但它作为public用户的select权限仍在,所以可以进行select操作。如果要使该用户

不能进行select操作,则可以用deny或者对public的select权限进行revoke操作。

实验 3.3

(1)打开查询分析器,用sa登录数据库。

犝狊犲犛犮犺狅狅犾犌狅犆狉犲犪狋犲狏犻犲狑犵狉犪犱犲2000犪狊

犛犲犾犲犮狋犳狉狅犿狊狋狌犱犲狀狋狊狑犺犲狉犲犵狉犪犱犲='2000';

(2)

犝狊犲犛犮犺狅狅犾犌狅犌狉犪狀狋狊犲犾犲犮狋狅狀犵狉犪犱犲2000狋狅王二

Page 107: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

C H A P T E R 4

第4章 数据库事务

SQLServer2000数据库系统通过事务保证多个数据库操作在一起处

理,而事务使用锁定技术来防止其他数据库用户来更新或读取未完成事务

中的数据。同时,SQLServer数据库系统为了提高自身的性能,实现多用

户之间数据的共享,采用并发控制策略来实现多个任务的并行运行。

实验4.1 SQLServer中事务的定义

1. 实验目的

  熟悉SQLServer的事务控制语言,能够熟练使用事务控制语言来编

写事务处理程序。

2. 原理解析

1)事务的概念

事务(transaction)是一组单一逻辑工作单元的操作集合,是用户采用

高级数据 操 纵 语 言 或 编 程 语 言 书 写 的 用 户 程 序,并 由 事 务 开 始(begintransaction)和事务结束(endtransaction)来界定全体操作的集合。

2)事务的性质

数据库管理系统为了实现数据库系统的完整性,事务的ACID性质是

数据库事务处理的基础,它要求事务具有以下的性质:(1)原子性(atomicity):要求事务的全部操作要么在数据库中全部正

确地反映出来要么全部不反映。(2)一致 性(consistency):数 据 库 中 数 据 不 因 事 务 的 执 行 而 受 到 破

坏,事务执行的结 果 应 当 使 得 数 据 库 由 一 种 一 致 性 达 到 另 一 种 新 的 一 致

性。数据的一致性保证数据库的完整性。(3)隔离性(isolation):事务的并发执行与这些事务单独执行的结果

一样。也就是说,在多个事务并发执行时,各个事务不必关心其他事务 的

Page 108: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

101  第4章 数据库事务

执行,如同在单个用户环境下执行一样。事务的隔离性是事务并发控制技术的基础。(4)持久性(durability):事 务 对 数 据 库 的 更 新 应 永 久 地 反 映 在 数 据 库 中。也 就 是

说,一个事务一旦完成其全部操作之后,它对数据库所有更新操作的结果将在数据库中永

久存在,即使以后发生故障也应保留这个事务的执行结果。持久性的意义在于保证数据

库具有可恢复性。

3)事务的控制

事务的操作可由事务开始、事务读写、事务提交、事务回滚若干个基本操作组成,SQLServer提供事务控制语法,来将SQLServer语句集合分组后形成单个的逻辑工作单元,

每个单元都是一个独立的事务。表4.1是事务控制语句语法及含义。

表4.1 事务控制语句语法及含义

事务控制语句语法 事务控制语句的含义

BEGINTRAN 表示事务开始执行

COMMITTRAN 表示事务完成 所 有 数 据 操 作,同 时 保 存 操 作 结 果,它 标 志 着

事务的成功完成

ROLLBACKTRAN 表示事务未完 成 所 有 数 据 操 作,重 新 返 回 到 事 务 开 始,它 标

志着事务的撤销

SAVETRAN 表示完成部分事务,同时撤销事务的其他部分

在事 务 控 制 中 常 常 还 需 要 通 过 检 测 两 个 全 局 变 量 @ @ERROR 和 @ @TRANCOUNT来检测事务的状态。

全局变量@@ERROR记录任何TransactSQL语句中的最近错误。如果语句成功

执行,变量值为0;如果语句执行失败,变量值不为0。在事务定义处理时,往往需要检查

@@ERROR来 判 断 语 句 执 行 是 否 成 功。如 果 没 有 成 功,则 需 要 使 用 ROLLBACKTRAN语句来撤销事务。

全局变量@@TRANCOUNT记录SQLServer当前等待提交的事务数,如果没有等

待提交的事务数,全局变量@@TRANCOUNT的值为0。

4)事务的类型

对数据库的访问是建立在对数据“读”和“写”两个操作之上的,因此,一般事务中涉及

数据操作主要是由“读”与“写”语句组成,而当事务仅由读语句组成时,事务的最终提交就

会变得十分简单。因此,有时可以将事务分成只读型和读写型两种。(1)只读型(ReadOnly)

此时,事务对数据库的操作只能是读语句,这种操作将数据X由数据库中取出读到

内存的缓冲区中。定义 此 类 型 即 表 示 随 后 的 事 务 均 是 只 读 型,直 到 新 的 类 型 定 义 出 现

为止。(2)读写型(Read/Write)

此时,事务对数据库可以做读与写的操作,定义此类型后,表示随后的事务均为读/写

型,直到新的类型定义出现为止。此类操作可以默认。

上述两种类型可以用下面的SQL语句定义:

Page 109: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

102  数据库系统实验指导教程

犛犈犜犜犚犃犖犛犃犆犜犐犗犖犚犈犃犇犗犖犔犢犛犈犜犜犚犃犖犛犃犆犜犐犗犖犚犈犃犇犠犚犐犜犈

3. 实验内容

事务编程是数据库应用系统中经常要用到的技术,通过使用事务控制语言和SQL语

句实现各种事务操作。

4. 实验步骤

1)要求

假设对于某数据库用户在银行有两个账号,分别为A账号和B账号,现在需要从A账号转1000元到B账号,编写事务处理程序,实现这一操作。

(1)采用隐式事务方式来实现事务编程。(2)采用显示用户定义事务的方式来实现事务编程。(3)事务与批命令。(4)嵌套事务的编程。(5)在存储过程、触发器中使用事务编程。(6)命名事务与事务保存点。

2)分析与解答

(1)AutoCommit事务是SQLServer默认事务方式。它指出每条SQL语句都构成

事务,隐含事务的开始与结束控制点。如果在执行语句时遇到错误,则撤销操作,否则提

交并保存操作的结果(见4_1_1.sql)。

狌狆犱犪狋犲犪犮犮狅狌狀狋狊狊犲狋犫犪犾犪狀犮犲=犫犪犾犪狀犮犲-1000狑犺犲狉犲犪犮犮狅狌狀狋狀狅='犃'

狌狆犱犪狋犲犪犮犮狅狌狀狋狊狊犲狋犫犪犾犪狀犮犲=犫犪犾犪狀犮犲+1000狑犺犲狉犲犪犮犮狅狌狀狋狀狅='犅'

思考一下,这样的事务编程是否会造成数据库状态的不一致呢?

由于上面的代码中采取的隐式事务方法,如果对账户 A更新成功,就会自动向数据

库提交。假若后来对账户B的更新未能成功,则就造成转账失败,但前一操作已经提交,无法还原。这就可能会造成对数据库状态与事实语义不一致。

针对这一问题,需要设法将这若干条SQL语句组合成一个独立的事务,这样才能保

证各个操作步骤要么同时成功,要么一起失败。这就需要在操作步骤中使用显式事务的

方式来处理事务。(2)为了完全控制事务并定义多个操作步骤组成的逻辑工作单元,可以采用显示用

户定义事务的方式来实现用户期望的逻辑操作(见4_1_2.sql)。

犫犲犵犻狀狋狉犪狀狌狆犱犪狋犲犪犮犮狅狌狀狋狊狊犲狋犫犪犾犪狀犮犲=犫犪犾犪狀犮犲-1000狑犺犲狉犲犪犮犮狅狌狀狋狀狅='犃'

狌狆犱犪狋犲犪犮犮狅狌狀狋狊狊犲狋犫犪犾犪狀犮犲=犫犪犾犪狀犮犲+1000狑犺犲狉犲犪犮犮狅狌狀狋狀狅='犅'

犲狀犱狋狉犪狀

(3)批处理是由一条或多条TransactSQL语句或命令组成的,它能够成组地运行,

Page 110: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

103  第4章 数据库事务

用于向SQLServer提交成组的TransactSQL的语句组,由Go语句来终止语句组。批

处理经过整体编译一次成为一个执行计划,并一次将整个执行计划执行完毕。注意除非

批命令没有固有的事务性质,除显示的定义由几个语句构成的单个事务,否则批命令中的

每条语句都是一个互相独立的事务,每条语句单独完成或者失败,而且批命令中的一个事

务失败,不会影响其他语句的执行。利用查询分析器中执行如下代码(见4_1_3.sql):

犝狆犱犪狋犲犮狅狌狉狊犲狊狊犲狋犺狅狌狉=96狑犺犲狉犲犮犻犱='10001 '

犐狀狊犲狉狋狋犲犪犮犺犲狉狊狏犪犾狌犲狊('1234567890','犕犢','犕犢@犣犛犝.犈犇犝.犆犖',3000)

犛犲犾犲犮狋狋狅狆10犳狉狅犿狋犲犪犮犺犲狉狊(犎狅犾犱犔狅犮犽)

犌狅

打开事件探查器以标准的SQLProfilerStandard模板来记录SQLServer所发生的

事件,得到图4.1的结果,显示只有一条SQLBatchCompleted语句,说明批处理是一个

完整的执行计划,并且要将整个执行计划执行完毕。

图4.1 批处理操作跟踪

相反,如果没有GO语句,读者也可以通过事件探查器来观察一下,结果是每个语句

单独执行。(4)嵌套事务主要是为了支持存储过程中的一些事务,这些事务可以从事务中已有

的进程 中 调 用,也 可 以 从 没 有 活 动 的 事 务 进 程 中 调 用。嵌 套 事 务 对 于 COMMITTRANSACTION语句的每个调用都对应于最后执行的BEGINTRANSACTION语句,也就是最内层的事务。读者执行事务嵌套的实例代码如下(见4_1_4.sql),执行结果如

图4.2所示。

犛犈犔犈犆犜'犅犈犉犗犚犈犜犚犃犖犛犃犆犜犐犗犖:        '犃犛犎犐犖犜,@@犜犚犃犖犆犗犝犖犜犃犛犜犚犃犖犃犆犜犐犗犖犆犗犝犖犜犅犈犌犐犖犜犚犃犖犛犈犔犈犆犜'犜犎犈犉犐犚犛犜犜犚犃犖犛犃犆犜犐犗犖犛犜犃犚犜犛: '犃犛犎犐犖犜,@@犜犚犃犖犆犗犝犖犜犃犛犜犚犃犖犃犆犜犐犗犖犆犗犝犖犜

  犛犈犔犈犆犜犜犗犘3犉犚犗犕犆犎犗犐犆犈犛

  犅犈犌犐犖犜犚犃犖

   犛犈犔犈犆犜'犜犎犈犛犈犆犗犖犇犜犚犃犖犛犃犆犜犐犗犖犛犜犃犚犜犛:'犃犛犎犐犖犜,@@犜犚犃犖犆犗犝犖犜犃犛犜犚犃犖犃犆犜犐犗犖犆犗犝犖犜

  犆犗犕犕犐犜犜犚犃犖

  犛犈犔犈犆犜'犜犎犈犛犈犆犗犖犇犜犚犃犖犛犃犆犜犐犗犖犆犗犕犕犐犜犛'犃犛犎犐犖犜,@@犜犚犃犖犆犗犝犖犜犃犛犜犚犃犖犃犆犜犐犗犖犆犗犝犖犜犚犗犔犔犅犃犆犓犜犚犃犖

  犛犈犔犈犆犜'犜犎犈犉犐犚犛犜犜犚犃犖犛犃犆犜犐犗犖犚犗犔犔犅犃犆犓'犃犛犎犐犖犜,@@犜犚犃犖犆犗犝犖犜犃犛犜犚犃犖犃犆犜犐犗犖犆犗犝犖犜

(5)触发器是一种特殊类型的存储过程,主要用于完成数据库中对象的完整性。在

表中进行数据修改时自动执行,触发器被视为执行数据修改事务的一部分,与数据修改语

句在同一事务空间中执行。由于触发器已经在事务情境中操作,因此事务中要的事务控

制语句只有ROLLBACK或者SAVETRAN,用户不需要发出BEGINTRAN。下面的

Page 111: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

104  数据库系统实验指导教程

图4.2 事务嵌套的示例

代码为Courses的删除操作创建一个触发器,然后在数据表Courses执行删除操作,观察

事务数目的变化,如图4.3所示。在删除操作执行的过程中,触发器得到执行,而且事务

的数目为1,这就验证了触发器事务是数据修改事务的一部分(见4_1_5.sql)。

犆犚犈犃犜犈犜犚犐犌犌犈犚犜犇_犆犗犝犚犛犈犗犖犆犗犝犚犛犈犛犉犗犚犇犈犔犈犜犈

犃犛犇犈犆犔犃犚犈@犐犖犉犗犞犃犚犆犎犃犚(255)

犛犈犔犈犆犜@犐犖犉犗='触发器中的事务数据为:'+犆犗犖犞犈犚犜(犞犃犚犆犎犃犚(2),@@犜犚犃犖犆犗犝犖犜)

犘犚犐犖犜@犐犖犉犗犚犈犜犝犚犖

图4.3 触发器与事务的示例

同样可以从事务中调用存储过程,也可以在存储过程中启动事务,而且这是经常在数

据库开发过程中应用到的,因为在存储过程中使用事务,可以提高数据库操作的效率,可

以方便维护。在企业管理器中创建存储过程如图4.4所示。(6)命名事务与保存点事务。在编写大的存储过程、长的批处理、以及大量事务嵌套的时候,一个常见的问题就是

代码的可读性差。为了改进代码的可读性,在进行事务编程的时候,可以对事务进行命名

来清晰地标识事务,提示用户代码的逻辑性。

Page 112: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

105  第4章 数据库事务

图4.4 在存储过程中使用事务进行数据库的操作示例

命名事务是通过在BEGINTRAN语句中为事务命名,来标志整个事务逻辑的工作

单元。通过对事务命名,使得每个事务都易于识别,这对于事务嵌套更加重要。下面定义两个事务,内层事务更新表Courses,外层事务用于更新表Choices,通过事

务命名的方法来增加对事务的可读性,代码如下(见4_1_6.sql):

犅犈犌犐犖犜犚犃犖狋狉犪狀_狌狆犱_犮狅狌狉狊犲狊

狌狆犱犪狋犲犮狅狌狉狊犲狊

狊犲狋犺狅狌狉=60

狑犺犲狉犲犮犻犱='10051'

犅犈犌犐犖犜犚犃犖狋狉犪狀_狌狆犱_狋犲犪犮犺犲狉狊

  犻狀狊犲狉狋犻狀狋狅狋犲犪犮犺犲狉狊

  狏犪犾狌犲狊('1234567890','狕狊','犿狔@狕狊狌.犲犱狌.犮狀',3000)

犐犉@@犈犚犚犗犚!=0

  犅犈犌犐犖

   撤销事务

  犚犗犔犔犅犃犆犓犜犚犃犖狋狉犪狀_狌狆犱_狋犲犪犮犺犲狉

  犘犚犐犖犜'更新教师表失败'

  犚犈犜犝犚犖

  犈犖犇

Page 113: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

106  数据库系统实验指导教程

提交内层事务

犆犗犕犕犐犜犜犚犃犖狋狉犪狀_狌狆犱_狋犲犪犮犺犲狉狊

提交外层事务

犆犗犕犕犐犜犜犚犃犖狋狉犪狀_狌狆犱_犮狅狌狉狊犲狊

给事务命名 的 另 一 种 方 法 是 事 务 保 存 点,事 务 保 存 点 提 供 一 种 在 事 务 中 标 记 用

ROLLBACK撤销事务工作点的方法。利用事务保存点,可提交事务开始处至保存点的

部分事务,而将事务的其他部分撤销。代码如下(见4_1_7.sql):

犅犈犌犐犖犜犚犃犖狋狉犪狀_狌狆犱_犮狅狌狉狊犲狊

狌狆犱犪狋犲犮狅狌狉狊犲狊

狊犲狋犺狅狌狉=45

狑犺犲狉犲犮犻犱='10051'

设置事务保存点

犛犃犞犈犜犚犃犖狋狉犪狀_狌狆犱_犮狅狌狉狊犲狊_犱狅狀犲

犻狀狊犲狉狋犻狀狋狅狋犲犪犮犺犲狉狊

狏犪犾狌犲狊('1234567890','狕狊','犿狔@狕狊狌.犲犱狌.犮狀',3000)

犐犉@@犈犚犚犗犚!=0犗犚@@犚犗犠犆犗犝犖犜>1

  犅犈犌犐犖

   撤销事务

  犚犗犔犔犅犃犆犓犜犚犃犖狋狉犪狀_狌狆犱_狋犲犪犮犺犲狉狊_犱狅狀犲

  犘犚犐犖犜'更新教师表失败!'

  犚犈犜犝犚犖

  犈犖犇

提交事务

犆犗犕犕犐犜犜犚犃犖狋狉犪狀_狌狆犱_犮狅狌狉狊犲

5. 习题

(1)编写事务处理程序在School数据库的Courses表中分别插入记录。(2)编写事务处理程序在School数据库中更新Courses表中的指定记录。(3)编写事务处理程序在School数据库中删除Courses表中的指定记录。(4)在查询分析器中或者客户程序中调用存储过程。

实验4.2 SQLServer2000事务与锁

1. 实验目的

前面提到,在SQLServer2000这 个 多 用 户 数 据 库 中,为 了 实 现 多 个 用 户 的 同 时 存

取,需要对数据库对象进行锁定。实验将帮助读者深入了解事务执行过程中,SQLServer数据库是如何实现对数据行、索引页等资源一次只让一个用户使用的。

Page 114: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

107  第4章 数据库事务

2. 原理解析

事务的并发执行是数据共享性的重要保证,但并发执行应当加以适当控制,否则就会

出现数据不一致现象,破坏数据库的完整性,可能会产生以下问题:

1)丢失修改

丢失修改(LostUpdate)是 指 两 个 事 务T1和T2从 数 据 库 读 取 同 一 数 据 并 进 行 修

改,其中事务T2提交的修改结果破坏了事务T1提交的修改结果,导致了事务T1的修改

被丢失。丢失修改是由于两个事务对同一数据并发地进行写入操作所引起的,因而称为

写写冲突(WriteWriteConflict)。

2)读“脏”数据

读“脏”数据(DirtyRead)是指事务T1将数据a修改成数据b,然后将其写入磁盘;此后事务T2读取该修改后的数据,即数据b;接下来T1因故被撤销,使得数据b恢复到

了原值a。这时,T2得到的数据就与数据库内的数据不一致。这种不一致或者不存在的

数据通常就称为“脏”数据。读“脏”数据是由于一个事务读取的另一个事务尚未提交的数据所引起的,因而称之

为读写冲突(ReadWriteConflict)。

3)不可重复读取

不可重复读(NonrepeatableRead)是指当事务T1读取数据a后,事务T2进行读取

并进行更新操作,使得T1再读取a进行校验时,发现前后两次读取值发生了变化,从而

无法再读取前一次读取的结果。不可重复读也是由读写冲突引起的。为了保证数据库中事务并发执行的正确性,其根本原则就是实现事务并发执行的“可

串行化准则”,可串行化是对并发事务调度的一种评价手段,实际应用中还必须寻求一种

灵活、有效和可操作的封锁等技术手段保证调度的可串行化。封锁是指任何事务T在对某数据操作之前,先向系统发出请求对其加锁。加锁后事

务T就对该数据拥有了一定的控制权,在事务T释放锁之前其他事务不能更新该数据,通过这种手段保证数据库数据的完整性。

3. 实验内容

在SQLServer的事务与锁的实验部分,读者验证单个事务执行过程中锁的获得与释

放的过程,并加深对锁类型与锁定资源的认识与理解。通过实验验证多个事务并发执行

过程中,可能产生的数据不一致现象。

4. 实验步骤

1)要求

(1)编写事务程序,用于更新courses表中database课程的信息,观察其事务过程中

锁的获得与释放的情况,以及锁定资源的类型。(2)分析通过锁定查看的结果,结合实际分析。

Page 115: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

108  数据库系统实验指导教程

2)分析与解答

SQLServer数据库管理系统通过组件SQLServerLockManager来提供用户事务

进程之间的锁的自动分配,保证数据库资源(数据页、索引页、表、索引、数据库)的当前用

户的特定操作从开头到结束都拥有这些资源的一致视图,从而保证事务的一致性。锁管理器(LockManager)会根据不同的事务类型分配适当锁类型(如共享、排他、更

新等)和锁粒度(行、页、表等),并且保证访问资源的锁类型之间的相容性,防止和消除死

锁,并将锁自动调整。可以通过SQLServer数据库管理系统的提供的工具,来监视和跟踪SQLServer中

的锁活动信息。常见的方法有:

使用sp_lock存储过程;

使用企业管理器查看锁信息;

使用SQLProfile查看锁信息。(1)使用sp_lock存储过程。通过查询分析器打 开 一 数 据 库 连 接,编 写 更 新courses表 中 的database课 程 信 息。

注意事务没有提交语句,这主要是便于观察更新表的过程中锁资源的分配情况。更新事务语句如下:

犫犲犵犻狀狋狉犪狀狌狆犱犪狋犲犮狅狌狉狊犲狊狊犲狋犺狅狌狉=80狑犺犲狉犲犮犻犱='10001'

注意:为了便于观察锁的信息,事务没有提交或返回。通过查询分析器打开另一个数据库连接,执行

犈狓犲犮狊狆_犾狅犮犽犌狅

返回SQLServer中所有进程的信息,其输出样本如图4.5所示。

图4.5 用sp_lock查看SQLServer进程中的锁信息

如果要查看指定进程的spid,则仅仅显示与spid相关的锁,如图4.6所示。

图4.6 用sp_lock查看SQLServer指定进程的锁信息

Page 116: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

109  第4章 数据库事务

这就是更新事务执行过程中,获得锁的信息。

spid是更新事务的进程ID。

dbid是持有锁的数据库ID。

Objid是持有锁的表索引ID,0表示数据行、数据页、表或数据库的锁;1表示聚簇索

引数据行、索引行或索引页的锁。2~254表示非聚簇索引行或页的锁;255表示text或

image页的锁。

Type是资源持有锁的锁粒度类型,表明SQLServer加锁的资源对象的粒度。封锁

的封锁粒度(Granularity)是指实行事务封锁的数据目标的大小。在关系数据库中封锁粒

度一般有如下几种。

属性(值)。

属性(值)集合。

元组。

关系表。

物理页面。

索引。

关系数据库。

从上面七种不同粒度中可以看出,事务封锁粒度有大有小。一般而言,封锁粒度小则

并发性高但开销大,封锁粒度大则并发性低但开销小。综合平衡不同需求、合理选取封锁

粒度是非常重要的。如果在一个系统中能同时存在不同大小的封锁粒度对象供不同事务

选择使用,应当说是比较理想的。一般来说,一个只处理少量元组的事务,以元组作为封

锁粒度比较合适;一个处理大量元组的事务,则以关系作为封锁粒度较为合理;而一个

需要处理多个关系的事务,则应以数据库作为封锁粒度最佳,表4.2详细说明锁定资源的

粒度。

表4.2 SQLServer数据库锁定资源的粒度说明

资源 描  述

RID 数据行标识符。用于单独锁定表中的一行

Key 索引中的行锁。用于保护可串行事务中的键范围

Pag 8KB的数据页或索引页

Ext 相邻的八个数据页或索引页构成的一组

IDX 索引

TAB 包括所有数据和索引在内的整个表

DB 数据库

Resource是持有锁的资源对象的内部名称,读者可以在master数据库的syslockinfo表中找到。

Page 117: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

110  数据库系统实验指导教程

Mode是事务请求的锁的类型。SQLServer的锁定类型如表4.3所示。

表4.3 SQLServer数据库锁定类型说明

类 型 描 述 类 型 描 述

S 共享锁定 SchS 结构稳定锁

E 独占锁定 SchM 结构修改锁

IS 意图共享锁 U 更新锁定

IX 意图独占锁 BU 批量更新锁定

SIX 共享意图独占锁 X 排他锁

Status:锁定请求状态。GRANT表示提供锁定;WAIT表示锁定被另一进程保持

的锁定阻止;CNVT表示锁定要改变模式。

(2)用SQLEnterpriseManager浏览锁活动。

使用sp_lock查看锁活动信息时,显示的是数据库对象的ID值。在SQLEnterprise

Manager中 提 供 了 系 统 与 用 户 对 象 锁 进 程 的 直 观 显 示。读 者 通 过 展 开 管 理 文 件 夹,

展 开 当 前 活 动 项 目,并 单 击 或 者 就 可 以 显 示SQLServer中 的 锁

信 息。

单击 可以在右窗口中显示SQLServer中当前持有锁的进程的快照。单

击其中任何一个进程,可以列出进程当前所持有的锁,如图4.7所示。

图4.7 SQLEnterpriseManager浏览锁活动

单击 可以在右窗口中显示SQLServer中当前持有锁的对象快照,单击一个

对象可以列出这个对象当前持有的锁,如图4.8所示。

图4.8 SQLEnterpriseManager查看数据库对象当前持有的锁

(3)用事件探查器浏览锁活动。

事件探查器专门用于捕捉数据库中一些重要的事件,对于锁活动,事件探查器也提供

了一些锁事件,用于在跟踪中捕获,如表4.4所示。

Page 118: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

111  第4章 数据库事务

表4.4 锁活动事件及意义说明

事  件 意  义

Lock:Acquired 表示何时取得数据页或行等资源的锁

Lock:Cancel 表示何时取消所取得资源的锁

Lock:DeadLock 表示两个或多个并发进程何时形成相互死锁

Lock:Escalation 表示低级锁何时升级为高级锁

Lock:Release 表示进程释放以前所取得的锁资源

Lock:Timeout 选择资源锁请求超时

SQLProfiler还能提供对监视事件显示的数据值,如表4.5所示。

表4.5 SQLProfiler监视事件显示的数据项意义

数 据 项 意  义

spid 产生事件的进程的进程IDEventClass 捕获的事件类型

Mode 捕获事件中涉及的锁类型

ObjectID 锁事件中涉及的对象IDObjectName 锁事件中涉及的对象名

IndexID 锁的相关索引IDTextData 产生锁事件的查询

LoginName 与进程相关联的登录名

ApplicationName 产生锁事件的应用程序名

同样,针对于实验4.1中的操作,利用事件探查器来观察锁事件。首先建立需要跟踪

事件的模板,通过在跟踪属性模板中的“事件”页面,选择锁类型事件;在“数据列”中,选

择需要关注的数据项名;然后保存为新的锁事件跟踪模板,如图4.9所示。

图4.9 事件探查器设置待观察的锁事件

Page 119: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

112  数据库系统实验指导教程

其次,通过新建“跟踪”,在另一个连接中执行更新事务。在SQLServer的事件探查

器中,可以看到事务执行过程中锁事件的变化,如图4.10所示。

图4.10 事件探查器观察的锁事件

5. 习题

分别利用执行SP_Lock系统存储过程、SQLServer企业管理器、SQLServer事件探

查器来观察更新事件中锁的获取与释放的过程,并解释观察到的字段意义。

实验4.3 SQLServer2000事务与隔离级别

1. 实验目的

  前面提到,在SQLServer2000这个多用户数据库中,为了实现多个用户的同时 存

取,需要对数据库进行锁定。读者通过本实验学会控制SQLServer的锁的方法,深入理

解SQLServer是如何通过设置事务隔离级别来导致锁定的保持与释放。

2. 原理解析

1)通过设置隔离级别来控制锁定策略

SQLServer为了实现多用户的数据共享,支持标准SQL中定义的四种隔离级别,以

实现不同正确程度的并发控制。四种隔离级别分别如下:

未提交读(READUNCOMMITED):事务隔离的最低级别,仅可保证不读物理损

坏的数据。

提交 读(READCOMMITED):SQLServer的 默 认 级 别,可 以 保 证 不 读“脏”数据。

可重复读(REPEATABLEREAD):可以保证读一致性。

可串行化(SERIALIZABLE):事务隔离的最高级别,事务之间完全隔离,在该级

别上可以保证并发事务均是可串行的。事务必须运行在可重复读或更高隔离级别上才可防止丢失更新。设置隔离级别的命令是:

犛犈犜犜犚犃犖犛犃犆犜犐犗犖犐犛犗犔犃犜犐犗犖犔犈犞犈犔{犚犈犃犇犆犗犕犕犐犜犈犇|犚犈犃犇犝犖犆犗犕犕犐犜犈犇|犚犈犘犈犃犜犃犅犔犈犚犈犃犇|犛犈犚犐犃犔犐犣犃犅犔犈}

SQLServer提供的隔离级别的功能是针对系统当前用户连接所做的配置,影响事务

中锁的生命周期;但 是 如 果 事 务 隔 离 级 别 比 较 高,将 会 降 低 数 据 库 系 统 的 并 发 执 行 的

效率。

Page 120: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

113  第4章 数据库事务

2)利用锁定提示来控制锁定策略

与此同时,系统中可能也有其他的事务并不需要高的隔离级别,这时,可以使用锁定

提示语句,将事务语句的隔离级别的影响范围针对所配置的SQL的语法,采用在查询语

句加锁的方式来保证事务并发控制的正确性,又不至于降低系统的效率。通过SQLServer的封锁操作是在相关语句的“WITH (table_hint)子句”中完成的,

可用在SELECT、INSERT、UPDATE、DELETE等语句中指定表级锁定的方式和范围。常用的封锁关键词有:

(1)TABLOCK:对表施加共享锁,在读完数据后立即释放封锁,它可以避免读“脏”数据,但可能引起不可重复读问题。

(2)HOLDLOCK:与TABLOCK一起使用,可将共享锁保留到事务完成,而不是在

读完数据后立即释放,它可以保证数据的可重复读。(3)NOLOCK:不施加任何封锁,仅用于SELECT语句,它会引起读“脏”数据。(4)TABLOCKX:对表施加排他锁。(5)UPDLOCK:对表中指定元组施加更新锁,这时其他事务可对同表中的其他无组

也施加更新锁,但不能对表施加任何锁。如对课程表施加共享锁,并且保持到事务结束时再释放封锁:

犛犈犔犈犆犜犉犚犗犕犆犗犝犚犛犈犠犐犜犎(犜犃犅犔犗犆犓犎犗犔犇犔犗犆犓)

可以使用的锁的类型主要有:

NOLOCK可以使用SELECT、INSERT、UPDATE和 DELETE语句指定表级锁定

提示的范围,以引导 MicrosoftSQLServer2000使用所需的锁类型。当需要对对象所获

得锁类型进行更精细控制时,可以使用表级锁定提示。这些锁定提示取代了会话的当前

事务隔离级别。数据库作为共享资源,允许多个用户程序并行地存取数据。当多个用户

并行地操作数据库时,需要通过并发控制对它们加以协调、控制,以保证并发操作的正确

执行,并保证数据库的一致性。在SQL中,并发控制采用封锁技术实现,当一个事务欲对某个数据对象操作时,可申

请对该对象加锁,取得对数据对象的一定控制,以限制其他事务对该对象的操作。其语句

格式为:

犔犗犆犓犜犃犅犔犈表名(或表名集合)犐犖犛犎犃犚犈(或犈犡犆犔犝犛犞犈或犛犎犃犚犈犝犘犇犃犜犈)犕犗犇犈[犖犗犠犃犐犜]

其中,表名(或表名集合)中指出封锁对象,若为多个表名,则各个表名间以“,”相隔;任选

项NOWAIT表示多个用户要求封锁相同的关系时,后来提出的要求会被立即退回去,否则会等待该资源释放。

注意:控制SQLServer的锁既可以通过设置隔离级别来改变特定连接持有共享锁

或排他锁的时间长短,也可以利用锁定提示对连接的锁定策略加以调节,不过锁定提示只

对一个查询中的一个表起作用。

3)给表的索引实行锁控制

利用索引为单位也可以控制锁,与隔离级别和锁定提示不同的是,索引是从表的角度

来控制锁,而隔离级别和锁定提示是从连接与查询的角度来控制锁的。

Page 121: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

114  数据库系统实验指导教程

用户首 先 可 以 通 过 执 行sp_helptablename找 出 主 键 索 引 的 名 字,然 后 利 用sp_

indexoption系统存储过程来设置索引选项。其语法格式为:

犈犡犈犆狊狆_犻狀犱犲狓狅狆狋犻狅狀索引名

3. 实验内容

(1)采用本书提供的示范数据库School中的Teachers表进行实验,注意表中教师编

号为200003125的教师记录,将通过这个记录的修改进行实验。

① 设置末提交读隔离级别,实现脏读和不重复读。

② 设置提交读隔离级别,避免脏读,实现不重复读。

③ 设置可重复读,用来避免脏读、不可重复读,但不能避免幻像读。

④ 设置可串行读隔离级别。(2)模拟一个火车站的售票事务处理,通过语句提示锁定方式来提高系统的效率。(3)在索引级别粒度上实现对数据库对象的加锁或者解锁。

4. 实验步骤

(1)修改数据库School的Teachers表中教师记录。

① 在查询分析器中新建两个连接,在连接1中执行如下代码(见4_3_1.sql),实现在

事务中更新数据,延时一定的时间后,回滚数据。

犅犈犌犐犖犜犚犃犖

犝犘犇犃犜犈犜犈犃犆犎犈犚犛犛犈犜犛犃犔犃犚犢=4200犠犎犈犚犈犜犐犇='200003125'

犠犃犐犜犉犗犚犇犈犔犃犢'00:00:20'延时20秒

犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈犜犐犇='200003125'

犚犗犔犔犅犃犆犓犜犚犃犖

连接2执行的代码如下(见4_3_2.sql):

犛犈犜犜犚犃犖犛犃犆犜犐犗犖犐犛犗犔犃犜犐犗犖犔犈犞犈犔犚犈犃犇犝犖犆犗犕犕犐犜犜犈犇

模拟实现脏读

犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈犜犐犇='200003125'

犐犉@@犚犗犠犆犗犝犖犜<>0

 犅犈犌犐犖

  犠犃犐犜犉犗犚犇犈犔犃犢'00:00:20'

  犘犚犐犖犜模拟实现不可重复读

  犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈犜犐犇='200003125'

 犈犖犇

连接2执行的结果如图4.11所示。从结果中可以看出,事务2第一次读到的数据4200是事务1没有提交的数据。当事

务2第二次去读数据时,事务1回滚了,所以读到的数据与第一次读到的数据是不一致

的。在这儿,第一次发生了数据脏读,第二次发生了不可重复读。发生“脏读”的原因就是

Page 122: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

115  第4章 数据库事务

图4.11 模拟实现脏读和不可重复读

在事务1的执行过程没有和事务2的执行过程相互隔离,导致事务2读取了事务1没有

确定提交的数据,在实际应用的情况下,这种情况应当避免。

② 在查询分析器中新建两个连接,在连接1中执行代码,用于在事务中实现两次重

复相同的查询,在连接2中更新记录。

连接1中执行代码为(见4_3_3.sql):

犛犈犜犜犚犃犖犛犃犆犜犐犗犖犐犛犗犔犃犜犐犗犖犔犈犞犈犔犚犈犃犇犆犗犕犕犐犜犜犈犇

初始状态

犅犈犌犐犖犜犚犃犖

犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈犜犐犇='200003125'

犐犉@@犚犗犠犆犗犝犖犜<>0

犅犈犌犐犖

犠犃犐犜犉犗犚犇犈犔犃犢'00:00:20'

犘犚犐犖犜模拟实现不可重复读

犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈犜犐犇='200003125'

犈犖犇

犚犗犔犔犅犃犆犓犜犚犃犖

连接2中执行的代码为(见4_3_4.sql):

犛犈犜犜犚犃犖犛犃犆犜犐犗犖犐犛犗犔犃犜犐犗犖犔犈犞犈犔犚犈犃犇犆犗犕犕犐犜犜犈犇

犝犘犇犃犜犈犜犈犃犆犎犈犚犛犛犈犜犛犃犔犃犚犢=4200犠犎犈犚犈犜犐犇='200003125'

连接1执行的结果如图4.12所示。从图4.12中看出,在连接1的同一事务中,读出同一个数据项的值时是不同的,这就

是不可重复读。

Page 123: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

116  数据库系统实验指导教程

图4.12 读出同一个数据项的值时是不同的

  ③ 在连接1中设置事务的隔离级别为可重复读,执行如下代码(见4_3_5.sql):

犛犈犜犜犚犃犖犛犃犆犜犐犗犖犐犛犗犔犃犜犐犗犖犔犈犞犈犔犚犈犘犈犃犜犃犅犔犈犚犈犃犇初始状态

犅犈犌犐犖犜犚犃犖犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈犜犐犇='200003125'

犐犉@@犚犗犠犆犗犝犖犜<>0犅犈犌犐犖犠犃犐犜犉犗犚犇犈犔犃犢'00:00:20'

犘犚犐犖犜模拟实现不可重复读

犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈犜犐犇='200003125'

犈犖犇犚犗犔犔犅犃犆犓犜犚犃犖

在连接2中执行如下代码(见4_3_6.sql):

犛犈犜犜犚犃犖犛犃犆犜犐犗犖犐犛犗犔犃犜犐犗犖犔犈犞犈犔犚犈犘犈犃犜犃犅犔犈犚犈犃犇犝犘犇犃犜犈犜犈犃犆犎犈犚犛犛犈犜犛犃犔犃犚犢=4200犠犎犈犚犈犜犐犇='200003125'

连接1中执行的结果如图4.13所示。当设置事务隔离级别为RepeatableRead后,同一事务内部对同一数据的读取,其结

果是一致的。但是在连续的两次读取时,可能会发生。

图4.13 设置事务的隔离级别为可重复读

Page 124: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

117  第4章 数据库事务

④ 设置可串行读隔离级别,就可以在数据集放置一个范围锁,可以防止其他用户在

事务完成之前更新数据集成或者将行插入到数据集内。在连接1内,执行下列代码(见4_3_7.sql),实现同一事务内连续的查询,事务的隔离

级别设置为可重复读。

狊犲狋狋狉犪狀狊犪犮狋犻狅狀犻狊狅犾犪狋犻狅狀犾犲狏犲犾狉犲狆犲犪狋犪犫犾犲狉犲犪犱犅犈犌犐犖犜犚犃犖犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈犜犐犇='300000000'

犐犉@@犚犗犠犆犗犝犖犜<>0犅犈犌犐犖犠犃犐犜犉犗犚犇犈犔犃犢'00:00:10'

犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈犜犐犇='300000000'

犈犖犇犚犗犔犔犅犃犆犓犜犚犃犖

在连接2内,执行下列代码(见4_3_8.sql),删除教师编码为300000000的教师记录,事务的隔离级别也设置为可重复读。

狊犲狋狋狉犪狀狊犪犮狋犻狅狀犻狊狅犾犪狋犻狅狀犾犲狏犲犾狉犲狆犲犪狋犪犫犾犲狉犲犪犱犱犲犾犲狋犲犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈犜犐犇='300000000'

如图4.14所示,结果发现,在连接1中执行结果两次查询的结果均相同,但事实上,结果显示的记录已被删除,所以发生了幻想读。

图4.14 设置可串行读隔离级别

这时提高事务并发的隔离级别,设置为可串行读。将连接1中的代码改变为(见4_3_

9sql):

狊犲狋狋狉犪狀狊犪犮狋犻狅狀犻狊狅犾犪狋犻狅狀犾犲狏犲犾狊犲狉犻犪犾犻狕犪犫犾犲

犅犈犌犐犖犜犚犃犖

犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈犜犐犇='300000000'

犠犃犐犜犉犗犚犇犈犔犃犢'00:00:10'

犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈犜犐犇='300000000'

Page 125: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

118  数据库系统实验指导教程

犚犗犔犔犅犃犆犓犜犚犃犖

在连接2中,执行如下代码(见4_3_10.sql):

狊犲狋狋狉犪狀狊犪犮狋犻狅狀犻狊狅犾犪狋犻狅狀犾犲狏犲犾狊犲狉犻犪犾犻狕犪犫犾犲犐犖犛犈犚犜犐犖犜犗犜犈犃犆犎犈犚犛犞犃犔犝犈犛('300000000','犃犃','犅犅犅@163.犆犗犕',5000)

执行结果为空,说明事务执行顺序是完全串行化的。(2)火车订票系统采取更新锁最为合适。假设售票信息存放在表R(日期,班次,座

号,状态)中,其中状态表明机票是滞售出,初值为NULL。程序代码为:

犇犈犆犔犃犚犈@犱犱犪狋犲狋犻犿犲,@狋犮犺犪狉(6),@狀犮犺犪狉(10),@狊犮犺犪狉(2)…//输入@犱,@狋,@狀犅犈犌犐犖犜犚犃犖犛犃犆犜犐犗犖犛犈犔犈犆犜@狀=座号犉犚犗犕犚犠犐犜犎(犝犘犇犔犗犆犓)犠犎犈犚犈日期=@犱犃犖犇班次=@狋犃犖犇状态犐犛犖犝犔犔

   犐犉犝犘犇犃犜犈犚犛犈犜状态='犢'犠犎犈犚犈座号=@狀犃犖犇日期=@犱犃犖犇班次

犆犗犕犕犐犜犜犚犃犖犛犃犆犜犐犗犖

   犈犔犛犈犚犗犔犔犅犃犆犓犜犚犃犖犛犃犆犜犐犗犖

   …

(3)利用索引进行锁定。首先执行:

犲狓犲犮狊狆_犺犲犾狆狋犲犪犮犺犲狉狊

得到表Teachers的索引对象的名称,如图4.15所示。

图4.15 利用索引进行锁定

执行:

犲狓犲犮狊狆_犻狀犱犲狓狅狆狋犻狅狀'狋犲犪犮犺犲狉狊.犘犓_犜犈犃犆犎犈犚犛','犪犾犾狅狑犘犪犵犲犔狅犮犽狊',犳犪犾狊犲

执行成功后,系统不允许Teachers的主键上加页级锁。

5. 习题

(1)书写命令对选课成绩表Choices实施一个共享锁,并且保持到事务结束时再释放

封锁。答案:

犛犈犔犈犆犜犉犚犗犕犆犎犗犐犆犈犛(犜犃犅犔犗犆犓犎犗犔犇犔犗犆犓)

(2)书写命令对课程信息表Courses加锁,防止在检查表的过程中有人修改表。

答案:

犛犈犔犈犆犜犉犚犗犕犆犗犝犚犛犈犛(犜犃犅犔犗犆犓犡)

Page 126: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

119  第4章 数据库事务

(3)在下列的事务处理中,在COMMITTRAN时持有什么锁呢?并解释锁的意义。

犅犈犌犐犖犜犚犃犖

犝狆犱犪狋犲犮狅狌狉狊犲狊狊犲狋犺狅狌狉=60狑犺犲狉犲犮犻犱='10001 '

犐狀狊犲狉狋狋犲犪犮犺犲狉狊狏犪犾狌犲狊('1234567890','犕犢','犕犢@犣犛犝.犈犇犝.犆犖',3000)

犛犲犾犲犮狋狋狅狆10犳狉狅犿狋犲犪犮犺犲狉狊(犎狅犾犱犔狅犮犽)

犆犗犕犕犐犜犜犚犃犖

实验4.4 锁冲突与死锁

1. 实验目的

  锁定机制是实现数据库事务的并发控制机制,是保证数据库的一致性、正确性、完整

性的前提。但是随着用户事务进程的增多,数据库系统可能会由于事务请求的锁类型与

资源的现有锁类型不兼容,出现锁争夺,甚至会出现死锁现象。在本节实验,读者将学会

识别锁冲突,学会检查和处理死锁。

2. 原理解析

当客户向数据库提交查询后,客户机可能会感觉到好像“死机”了,这就可能是发生了

锁争,当系统中出现锁争夺的时候,如果不想让进程永久的等待下去,解决的办法是通过

设置锁超时时间间隔。可以用SETLOCK_TIMEOUT命令设置时间间隔。

死锁是在关系数据库管理系统中的某组资源上发生了两个或多个线程之间循环相关

性时,由于各个线程之间互不相让对方所需要的资源而造成的。SQLServer中有循环死

锁和转换死锁两大类。

循环死锁是由于系统或用户进程之间彼此都只有得到对方持有的资源才能执行时发

生;在下图中,事务A持有数据库对象A的独占锁,事务B持有数据库对象B的锁,与此

同时,事务A请求在数据库对象B上的锁,事务B请求在数据库对象A上的锁,它们都

希望得到对方持有的锁,互不相让,从而形成死锁。而转换死锁发生在两个或多个进程在

事务中持有同一资源的共享锁,而且都需要将共享锁升级为独占锁,但都要待其他进程释

放这一共享锁时才能升级。在下图中,事务A和事务B均持有数据库对象A的共享锁,

与此同时,事务A与B都希望将各自持有的共享锁,转变为独占锁,此时它们进入等待之

中,陷入死锁状态。如图4.16所示。

SQLServer的Lock_Monitor进 程 大 约5秒 钟 检 测 一 次 系 统 的 死 锁。第 一 次 检 查

时,Lock_Monitor进程探测所有等待锁资源的进程。Lock_Monitor线程检查等待锁请

求清单,检查持锁进行和等锁进程之间是否存在循环锁请求。如果存在,SQLServer会

选择中止开始会话以来累计CPU时间最少的进程,也可能中止锁优先级较低的进程,来

解除死锁。

用户可以用SETDEADLOCK_PRIORITY语句影响死锁受害者的进程。

Page 127: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

120  数据库系统实验指导教程

图4.16 循环死锁与转换死锁

3. 实验内容

(1)设计实验造成事务对资源的争夺,试分析原因,并讨论解决锁争夺的办法。(2)设计实验制造事务之间的死锁,并分析造成死锁的原因。(3)讨论预防死锁的办法。

4. 实验步骤

(1)打开查询分析器的一个连接,更新courses表中的database的课时。利用查询分

析器打开另一个连接,查询courses表中的database的课时。先执行更新事务(为了制造锁争夺,更新事务没有提交),代码如下(见4_4_1.sql):

犫犲犵犻狀狋狉犪狀狌狆犱犪狋犲犮狅狌狉狊犲狊狊犲狋犺狅狌狉=80狑犺犲狉犲犮犻犱='10001'

再执行查询事务,代码如下(见4_4_2.sql):

犫犲犵犻狀狋狉犪狀狊犲犾犲犮狋犳狉狅犿犮狅狌狉狊犲狊狑犺犲狉犲犮犻犱='10001'

犲狀犱狋狉犪狀

这时,利用企业管理器中查看管理项目中的 ,发现更新事务进程获得了对数

据库对象的排他锁X,致使查询事务进程54得不到查询需要的共享锁,因为出现锁争夺,

Page 128: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

121  第4章 数据库事务

使查询事务进程54处于等待状态。如图4.17所示。

图4.17 事务进程的锁对象观察

要识别进程是否受阻,更直观的方法是在查询分析器中执行sp_who存储过程,检查

blk列,如果blk列的值为0,则这个进程没有发生阻塞。如果是任何非0值,则 会 话 受

阻,blk列的值就是造成阻塞的进程ID。例如:

犲狓犲犮狊狆_狑犺狅犵狅

如图4.18所示,从执行结果中看出,进程54被进行52阻塞。

图4.18 用sp_who存储过程识别事务进程是否受阻

如果 用 户 希 望 知 道 参 与 锁 争 夺 的 会 话 最 后 在 执 行 的 命 令,可 以 用 DBCCINPUTBUFFER命令,并以所涉及的进程ID作为参数。如图4.19所示。

图4.19 用DBCCINPUTBUFFER命令查看会话执行的命令

进一步,如果将查询事务写成:

狊犲狋犔犗犆犓_犜犐犕犈犗犝犜2000狊犲犾犲犮狋犳狉狅犿犮狅狌狉狊犲狊狑犺犲狉犲犮犻犱='10001'

按同样的方法执行,将得到下面的结果:

服务器:消息1222,级别16,状态50,行1已超过了锁请求超时时段。

这就表明,当设置好锁定超时时间后,锁定管理器将自动将解除锁的争夺。

Page 129: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

122  数据库系统实验指导教程

除了系统当发生资源的争夺会发生阻塞时,对于费时的查询或事务;不正确的事务

或事务隔离级别;事务未正确处理的情况下,也可能会发生阻塞。(2)采用本书范例中提供的数据库School,选择其中的TEACHERS来进行实验。通过查询分析器打开两个连接到SQLServer执行实例,在这两个连接中同时执行以

下的代码(见4_4_3.sql)。

犛犈犜犜犚犃犖犛犃犆犜犐犗犖犐犛犗犔犃犜犐犗犖犔犈犞犈犔犚犈犘犈犃犜犃犅犔犈犚犈犃犇犅犈犌犐犖犜犚犃犖犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈犜犐犇='200003125'

犠犃犐犜犉犗犚犇犈犔犃犢'00:00:05'

犝犘犇犃犜犈犜犈犃犆犎犈犚犛犛犈犜犛犃犔犃犚犢=4000犠犎犈犚犈犜犐犇='200003125'

犆犗犕犕犐犜犜犚犃犖犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈犜犐犇='200003125'

在其中一个连接中获得的错误消息,如图4.20所示。

图4.20 死锁的形成与解除1

在另一个连接中得到的结果如图4.21所示。

服务器:消息1205,级别13,状态50,行1

图4.21 死锁的形成与解除2

  消息1205是在SQLServer实例发现系统出现死锁情况时,会选择其中的一个程序

当作牺牲者,直接停掉 当 前 程 序 的 工 作,并 回 滚 之 间 所 做 的 事 务 内 容,并 通 过 连 接 回 传

1205的错误。

Page 130: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

123  第4章 数据库事务

在这个 实 验 中 为 什 么 会 出 现 死 锁 呢? 这 是 因 为 两 个 连 接 都 是 通 过 设 置 共 享 锁

(SharedLock)对同一数据查询,并接着尝试转换为更新锁(UpdateLock),进而到排他锁

(Exclusive)以完成更新操作,但是设置事务的隔离级别为可重复读,在事务完成之前,两

个连接不可能释放共享锁而永远无法更新,导致发生死锁。可以通过在执行的代码中加入一条:

犲狓犲犮狊狆_犾狅犮犽@@狊狆犻犱

来观察系统当前锁的分配情况。

52 8    0    0  犇犅          犛   犌犚犃犖犜52 1 85575343 2 犓犈犢 (犮300犱27116犮犳) 犛 犌犚犃犖犜52 8 469576711 0 犜犃犅 犐犡 犌犚犃犖犜52 1 85575343 2 犘犃犌1:1487 犐犛 犌犚犃犖犜52 1 85575343 0 犜犃犅 犐犛 犌犚犃犖犜52 8 469576711 1 犓犈犢 (犫700犮犲7犪3067) 犡 犌犚犃犖犜52 1 85575343 2 犓犈犢 (犫200犱犫犫63犪8犱) 犛 犌犚犃犖犜52 8 469576711 1 犘犃犌1:1657 犐犡 犌犚犃犖犜52 1 85575343 2 犓犈犢 (49014犱犮93755) 犛 犌犚犃犖犜52 1 85575343 2 犓犈犢 (犫犲00犱犱39306犲) 犛 犌犚犃犖犜52 1 85575343 2 犓犈犢 (170130366犳3犱) 犛 犌犚犃犖犜52 1 85575343 2 犓犈犢 (0701犱0犳犳1犫6犲) 犛 犌犚犃犖犜52 1 85575343 2 犓犈犢 (犮犪008636犱9犮0) 犛 犌犚犃犖犜

注意:当系统发生锁争夺时,如果有事务超时,SQLServer向用户返回错误号1222;当发生死锁时,如果有牺牲事务,SQLServer向用户返回错误号1205;读者在应用时,需要在应用程序中处理锁争夺与死锁,通过在错误处理器中捕获消息1222或1205,然后让

应用程序自动重新提交事务。(3)死锁发生后,对系统会产生不好的影响,读者要尽量有针对性的防止死锁并在死

锁发生时,及时的处理。一般来说,注意以下的事项:

① 当系统中锁定与阻塞增多时,有可能发生死锁,因此在应用程序中尽量防止或尽

快处理阻塞。

② 存取资源的顺序最好要相同。如连接A先存取甲数据库对象,再存取乙数据库对

象,如果连接B的存取顺序刚好相反,则有可能发生死锁。

5. 习题

(1)创建两个用户表Check和Save,如表4.6所示。

表4.6 用户表Check和Save

Check Save

字段名 类型 是否为空 字段名 类型 是否为空

Acco_num int Notnull Acco_num int NotnullAcco_name Varchar(20) Notnull Acco_name Varchar(20) Notnullbalance money Notnull balance money Notnull

Page 131: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

124  数据库系统实验指导教程

  (2)分别向表Check和Save中添加两条记录。

犻狀狊犲狉狋犮犺犲犮犽狏犪犾狌犲狊(1,'张三',500)

犻狀狊犲狉狋犮犺犲犮犽狏犪犾狌犲狊(2,'李四',300)

犻狀狊犲狉狋狊犪狏犲狏犪犾狌犲狊(1,'张三',100)

犻狀狊犲狉狋狊犪狏犲狏犪犾狌犲狊(2,'李四',300)

(3)分别打开两个连接,在连接1中执行从支票账户1加100元。

犅犈犌犐犖犜犚犃犖犝犘犇犃犜犈犆犎犈犆犓犛犈犜犅犃犔犃犖犆犈=犅犃犔犃犖犆犈+100犠犎犈犚犈犃犆犆犗_犖犝犕=1

在连接2中运行,将现金账户2转出200元。

犅犈犌犐犖犜犚犃犖犝犘犇犃犜犈犛犃犞犈犛犈犜犅犃犔犃犖犆犈=犅犃犔犃犖犆犈-200犠犎犈犚犈犃犆犆犗_犖犝犕=2

继续在连接1中运行,将现金账户1减去100元。

犝犘犇犃犜犈犛犃犞犈犛犈犜犅犃犔犃犖犆犈=犅犃犔犃犖犆犈-100犠犎犈犚犈犃犆犆犗_犖犝犕=1

请读者分析,将会发生什么情况呢?如何解决这个问题?

实验4.5 SQLServer2000的事务与事务日志

1. 实验目的

  SQLServer2000提供了强大的日志功能,事务日志可以用来帮助诊断系统的故障,帮助恢复系统。

2. 原理解析

1)日志的基本原理

日志以一种安全的方式记录数据库状态变迁的历史,它的作用是为了在发生系统故

障时重建对数据库所做的更新的过程。日志是关于日志记录的一个序列,每个日志记录

记载有关事务已做的事的某些情况。有两种日志方式可以用来记录数据库发生的变化,

它们是Redo日志和Undo日志。

UNDO日志 记 录 数 据 库 系 统 中 发 生 的 更 新 记 录 情 况。更 新 记 录 是 一 个 三 元 组

<T,X,V>,就是说事务T改变了数据库元素X,X元素原来的值为V。更新记录所反

映的改变通常发生在主存而不是磁盘上。因此可以使用 UNDO日志来恢复旧值来清除

事务可能在磁盘上造成的影响。UNDO日志的规则如下:

规则1:如果事务T改变了数据库元素X,那么形如<T,X,V>的日志记录必须在

X的新值写到磁盘前写到磁盘。

Page 132: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

125  第4章 数据库事务

规则2:如果事务提交,则其COMMIT日志必须在事务改变的所有数据库元素已写

到磁盘后再写到磁盘,但应尽快。

规则1、规则2指出了与事务相关的内容是按以下顺序写到磁盘的。

(1)记录所改变数据库元素旧值的日志记录。

(2)改变的数据库元素自身。

(3)COMMIT日志记录。

REDO日志也是记录数据库系统中发生的更新记录情况。不同于 UNDO日志的是

它用给出新值的日志记录来表示数据库元素的更新。这些记录也是三元组<T,X,V>,

不同的是这一记录的含义是:事务T为数据库元素X写入了新值V,每当一个事务T修

改数据库元素X的时候,形如<T,X,V>的一条记录就会写入了日志文件中。REDO日

志的规则如下:

(1)记录所改变数据库元素新值的日志记录。

(2)COMMIT日志记录。

(3)改变数据库元素的自身。

REDO日志是在数据库元素改变以前,将事务日志先写到 磁 盘 上,因 此 这 一 规 则 也

称为提前写日志规则。同 样,也 说 明 了 如 果 一 个 日 志 记 录 中 没 有<COMMITT>的 记

录,则说明事务T对数据库所做的更新没有写到磁盘上。

2)SQLServer的日志操作

SQLServer2000数据库用事务日志按修改发生的顺序记录数据库中发生的数据插

入、删除与更新操作,并存放在与数据库关联的一个或多个日志文件中。

SQLServer2000使用提前写日志的方法,缓存管理器先将数据库 的 改 变 写 入 事 务

日志文件,然后再将改变写入数据库中。其完整的事件发生顺序如下:

将BEGINTRAN记录写入缓冲内存的事务日志中。

将数据修改信息写入缓冲内存的事务日志中。

将数据修改信息写入缓冲内存的数据库中。

将COMMITTRAN记录写入缓冲内存的事务日志中。

将事务日志文件记录写入磁盘上的事务日志文件中。

将COMMIT确认发送到客户机进程中。

细心的读者会发现,在数据库修改的过程中,数据修改信息并没有直接写入了磁盘文

件中去,这是为了减少磁盘I/O;但是所有的日志要同步写入,保证日志记录实际写入磁

盘并按正确有顺序写入,从而保证日志记录的可靠性。

为了保证先写入日志记录再影响数据页,SQLServer2000记录改 变 的 数 据 页 的 日

志记录的日志序号(LSN),修改的数据页只能在数据页的LSN记录小于写入事务日志的

最后日志页的LSN时才写入磁盘。

3)事务日志与检查点

SQLServer2000 利 用 校 验 点 来 决 定 数 据 写 入 磁 盘 的 时 机。所 谓 检 查 点

Page 133: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

126  数据库系统实验指导教程

(CheckPoint),实际上是SQLServer2000的内置过程,用于在已知的、良好的时刻及时

准确的提交数据库的变化或配置选项的变化。

SQLServer2000提供两种检查点:

第一类检查点是SQLServer自动发出检 查 点。在 没 有 用 户 的 干 预 下,通 过 大 约 每

60秒发出一次检查点,这个时间也可以通过SQLServer设置以及根据数据库的性能调

整。另外当用户通 过 存 储 过 程sp_dboption修 改 数 据 库 参 数 或 者 关 闭 数 据 库 时,SQL

Server2000也会自动发出检查点。

第二类检查点是强 制 检 查 点,它 是 数 据 库 的 所 有 者 或DBA根 据 需 要 用checkponit语句发出的命令。SQLServer2000在关闭服务器时也会自动发出检查点,这 样 可 以 在

数据库关闭以前,将所有的数据库信息都保存在磁盘上,可以减少系统重新启动时的启动

时间。

如果SQLServer数据库意外中止,则下一次启动可能会花很长时间,这是因为系统

在意外中止时来不及发出检查点,数据库信息丢失。SQLServer在重新启动时,要将整

个事务回滚,直到最后一个检查点,以用于将数据库恢复到最后一个正确的状态。

3. 实验内容

在这部分实验中,将了解以下内容:

(1)学会利 用 DBCC命 令 来 读 写SQLServer2000,并 能 够 理 解 各 个 字 段 的 具 体

意义。(2)利用LumiCent公司推出的LogExplorer软件来观察日志的内容。(3)SQLServer2000错误日志的读取。

4. 实验步骤

(1)SQLServer2000日志的读取。

SQLServer在内部将每个物理事务日志文件分成多个虚拟文件,它是事务日志修剪

的单元,当一个虚拟日志文件不再为活动的事务包含记录时,则它被修剪,它所占的空间

可提供给新的事务记录日志。SQLServer的事务日志是一个环绕处理的日志,换句话说

当物理日志文件不只一个时,将循环地在每个物理日志文件上增长虚拟日志文件。另外,

MSSQLServer利用LSN(logsequencenumber)来标记每一个日志记录,LSN是一个唯

一、递增的标记,越早的活动记录,它的LSN就越小,因此,可以利用LSN来准确定位记

录。日志文件的读取对于检查数据库事务的详细历史,对于数据库的恢复有着很大的作

用。SQLServer数据库提供 了 日 志 读 取 的 接 口,可 以 使 用 这 些 接 口 获 取 日 志 中 的 有 用

信息。

SQLServer数据库提 供 标 准 的SQL命 令 来 获 取 事 务 的 日 志 信 息,但 是 可 以 通 过

SQLServer提供的DBCCLOG命令来读取内存中活动的事务日志记录。

① DBCCLOG命令:该命令用于显示特定的数据库事务日志。其语法为:

Page 134: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

127  第4章 数据库事务

犇犅犆犆犔犗犌({犱犫犻犱|犱犫狀犪犿犲},[,狋狔狆犲={-1|0|1|2|3|4}])

参数说明:

dbid|dbname:为相应的数据库名或数据库的ID。

type:输出的类型,如表4.7所示。

表4.7 DBCCLOG命令输出的类型

类型 说  明

0 默认值,提供最少的信息(operation,context,transactionid)

1 在0的基础上增加(flags,tags,row,description)

2 增加(objectname,indexname,pageid,slotid)

3 有关操作的所有信息但返回不是记录集

4 有关操作的所有信息以及记录的原始数据

-1 显示所有的信息,但返回不是记录集

② DBCCPAGE命令:该命令可以显示datapage的结构。其语法为:

犇犅犆犆犘犃犌犈({犱犫犻犱|犱犫狀犪犿犲},狆犪犵犲狀狌犿[,狆狉犻狀狋狅狆狋犻狅狀][,犮犪犮犺犲][,犾狅犵犻犮犪犾])

参数说明:

dbid|dbname:相应的数据库名或数据库的ID。

pagenum:页号,包括两个参数,如0001:00000034的参数应写成34,1。

printoption:输出类型,如表4.8所示。

表4.8 DBCCPAGE命令输出的类型

类型 说  明

0 默认值,只显示pageheader信息

1 显示pageheader信息、页中每一rowdata信息以及offsettable2 与1类似,只是把整个页信息不加分析地显示出来

cache与logical不作详细介绍。注意:使用该命令前必须先执行DBCCTRACEON(3604)。为了读取日志,可以利用日志读取接口DBCCLOG命令从内存中直接读写当前活动

的全部日志记录;然后将日志读取接口得到的最新日志记录集成按标准的数据结构进行

形式变换,形成要分析的日志记录。使用DBCCLOG命令,选择输出类型为4,得到原始

日志的记录集形式,而且还包含日志记录的原始二进制数据字段RecordData。执行:

犇犅犆犆犔犗犌(狊犮犺狅狅犾,4)

结果如图4.22所示。但RecordData存储的是一个长的二进制的数据流并以十六进制的形式显示出来的,

很难以直观的形式显示出来,除非利用进一步的日志分析软件,才能得到直观的结果。(2)通过LogExplorer来读取SQLServer的日志。

Page 135: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

128  数据库系统实验指导教程

图4.22 DBCCLOG分析得到的SQLServer数据库日志

LogExplorer是LumiCent推出的软件,它提供了在线日志的分析功能与日志文件

的分析功能,可以利用LogExplorer进行事务的重做与撤销。下面对它的在线日志分析

功能着重介绍,其操作步骤如下:第一步,单 击 LogExplorer左 窗 口 中 的 AttachLogFile选 项,在 右 窗 口 中SQL

Server服务器选项中选择SQLServer数据库服务器,选择登录方式,输入登录名和用户

密码,单击Connect按钮。如图4.23所示。

图4.23 LogExplorer主窗口

第二步,在图4.23中选择要查看日志的数据库名,在本例中,选择school数据库,并

且选择利用UseonlineLog,单击Attach按钮。如图4.24所示。第三步,在图Browse中,选择ViewLog,将在右边的上半部分看到十六进制形式的

事务日志,在右边的下半窗口部分,将可以看到日志的数据。如图4.25所示。

Page 136: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

129  第4章 数据库事务

图4.24 LogExplorer中日志文件的选择

图4.25 LogExplorer的日志浏览窗口

Page 137: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

130  数据库系统实验指导教程

  (3)SQLServer2000错误日志的读取。在SQLServer中保留有最近6个错误日志的档案,可以通过企业管理器直接浏览,

操作步骤如下:

选择要浏览的服务器,在“管理”文件夹中,选择 ,在右半窗口中将出

现错误日志的详细信息。选择错误日志中的日志条目,在企业管理器的右半窗口中将出现错误日志,在错误日

志中,在 Message列中会看到诸如failed,problem和unable的字样,用户可以密切关注

这些信息,经过仔细分析找到SQLServer数据库服务器出现的问题,并确定相应的解决

办法。

5. 习题

(1)现在许多开发商提供了功能完善的第三方软件如LogExplorer,有兴趣的读者

可以试着下载,来了解SQLServer的日志。(2)深入学习REDO和UNDO日志记录的原理及过程。

习题答案

实验 4.1

  (1)~(3)的代码分别参见4_1_8.sql、4_1_9.sql、4_1_10.sql,其中4_1_8.sql为插入

课程信息的存储过程,4_1_9.sql为更新课程信息的存储过程,4_1_10.sql为删除课程信

息的存储过程。(4)的代码见源代码文件夹中“通过应用程序调用存储过程”的文件夹。应用程序的

源代码是采用Delphi实现。实验4.2~4.5的习题答案略。

Page 138: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

数据库的备份与还原

C H A P T E R 5

第5章

由于计算机系统中硬件故障、软件错误、操作员失误以及恶 意 破 坏 是

不可避免的,这些故障轻则造成运行事务非正常中断,影响数据库的正确

性,重则破坏数据库,使数据库部分或全部数据丢失。DBMS必须 具 有 把

数据库从错误状态恢复到某一已知的正确状态的功能,这就是数据库的恢

复。恢复子系统是DBMS的一个重要组成部分,保证故障发生后能把数据

库中的数据从错误状态恢复到某一已知的正确状态,保证事务ACID。恢

复技术是衡量系统优劣的重要指标。而为了进行数据库恢复的重要基础,就是要存在数据库的各种备份。

实验5.1 SQLServer数据库的备份

1. 实验目的

  理解数据库的故障类型,并学会制定合适的备份计划,掌握多种备份

数据库的方法。

2. 原理解析

1)数据库的故障类型

数据库的故障包括以下三种情况:(1)事务故障。某个事务在运行过程中由于种种原因未运行至正常结束点就中断了,

造成数据库可能处于不正确状态。事务故障分为可预期的(应用程序可以

发现的故障,如违 反 了 某 些 完 整 性 限 制、输 入 数 据 有 误 等)和 不 可 预 期 的

(应用程序无法发现只能由系统处理的故障,如运算溢出、发生死锁等)两

大类。(2)系统故障。系统故障也称为软故障,指造成系统停止运转的任何事件。这类故障

Page 139: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

132  数据库系统实验指导教程

影响所有正在运行的事务,使得内存数据丢失,所有运行事务都异常终止,但不破坏整个

数据库。系统故障的原因:特定类型的硬件错误(如CPU故障)、操作系统故障、DBMS代码错误、突然停电等。

(3)介质故障。

介质故障也称为硬故障,使存储在外存中的数据部分丢失或全部丢失。介质故障发

生的可能性较小,但破坏性大,它能破坏数据库或部分数据库,并影响正在存取这部分数

据的所有事务。介质故障的原因有磁盘损坏、磁头碰撞、瞬时强磁场干扰等。

2)SQLServer支持的备份类型

(1)数据库备份

数据库备份是对一个数据库的完整备份。它将复制所有用户定义的对象、系统表及

数据。备份操作时,SQLServer把 所 有 完 成 的 事 务 写 到 磁 盘 上,然 后 开 始 复 制 数 据 库。

对于没有完成或者备份开始时没有发生的事务将不被复制。这种备份需要较大的存储空

间,也需要相对长的备份时间,因此,数据库备份的频率往往较低。

(2)差异备份。

差异备份也称为增量数据备份,仅仅备份最新数据备份以来发生变化的数据库的更

新。它同样不处理没有完成或者备份开始时没有发生的事务,它只提供将数据库恢复到

差异数据库创建时的能力,但不具备恢复到失效点的能力。由于它仅仅备份上一次备份

以来发生变化的数据库的更新,因此它的备份数据量比完全备份时要小而且速 度 也 快。

为了提供恢复到失效点的能力,将采用事务日志备份。

(3)事务日志备份。

事务日志备份将对上一次数据备份、差 异 备 份 或 者 事 务 日 志 备 份 以 来 所 有 发 生 并

已完成的事务日志进行记录。它与差异备份区别在于:差异备份记 录 数 据 库 所 有 的 变

化;事务日志备份记 录 日 志 操 作。事 务 日 志 备 份 能 够 提 供 让 系 统 恢 复 到 失 效 点 的 能

力,但是如果有事务日 志 文 件 的 损 坏 或 丢 失,则 数 据 库 系 统 也 能 将 数 据 库 还 原 至 丢 失

日志文件之前的最后 一 个 事 务 日 志 备 份。事 务 日 志 备 份 往 往 应 用 于 关 键 性 较 强 的 数

据库系统。

事务日志备份还可以截断事务日志的非活动部分。

(4)文件或文件组备份。

文件或文件组备份将对数据库中的个别文件或文件组备份。主要应用在系统没有足

够的时间用于完整备份、差异备份。这种情况下,可以每天备份一个文件组,并且对每个

文件组都执行一次事务日志备份,这样每个文件组每隔3天备份一次。如果丢失了所有

文件组,就可以从文件组备份和事务日志备份还原到最新的文件组。

3)SQLServer支持的数据备份策略

SQLServer的数据备份策 略 是 由 数 据 库 的 恢 复 与 还 原 模 型 决 定 的,详 细 情 况 参 见

表5.1。

Page 140: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

133  第5章 数据库的备份与还原

表5.1 SQLServer的数据备份策略

恢复模型 可选择的备份方式 优  点 工作损失表现 能否恢复到即时点

简单完 全 备 份、差 异

备份

允许高 性 能 大 容 量 复

制操作。

收回日 志 空 间 以 使 空

间要求最小

必 须 重 做 自 最 新 的 数

据 库 或 差 异 备 份 后 所

发生的更改

可 以 恢 复 到 任 何

备 份 的 结 尾 处。

随后必须重做更改

完全

完全 备 份、差 异 备

份、事 务 日 志 备

份、文 件 或 文 件 组

备份

数据文 件 丢 失 或 损 坏

不会导致工作损失。

可以恢 复 到 任 意 即 时

点(例 如,应 用 程 序 或

用户错误之前)

正常情况下没有。

如 果 日 志 损 坏,则 必

须 重 做 自 最 新 的 日 志

备份后所发生的更改

可 以 恢 复 到 任 何

即时点

大容量日志

完全 备 份、差 异 备

份、事 务 日 志 备

份、文 件 或 文 件 组

备份

允许高 性 能 大 容 量 复

制操作。

大容量 操 作 使 用 最 少

的日志空间

如 果 日 志 损 坏,或 者

自 最 新 的 日 志 备 份 后

发 生 了 大 容 量 操 作,

则 必 须 重 做 自 上 次 备

份后所 做 的 更 改。否

则不丢失任何工作

可 以 恢 复 到 任 何

备 份 的 结 尾 处。

随后必须重做更改

4)影响备份计划的因素

为了保证数据库的安全,需要制定良好的数据库备份计划。影响备份计划的因素包

括:数据库的长度、备份的媒介、数据库的可用性和数据库的恢复窗口等。备份媒体是备份设备用来存储数据备份的实际物理存储。备份媒体可以是磁盘,也

可以是磁带。备份媒体集可以包含一个或多个备份集并描述那些备份集所用的 全 部 媒

体,与所用媒体或备份设备的数量无关。例如,如果使用四个磁带备份设备创建数据库备

份,每个磁带备份设备使用五盘磁带存储备份,则媒体集包含20盘磁带。媒体家族描述用于单个备份集的单个备份设备所用的全部媒体。在前面的示例中有

四个媒体家族,每个媒体家族由每个磁带备份设备所用的五盘磁带组成。(1)初始化媒体与延续媒体:初始化媒体是媒体家族中的第一个媒体。如果在备份

过程中初始化媒体已满,将使用其他的媒体,直到完成备份操作为止。媒体家族中除初始

化媒体外的所有其他媒体都称为延续媒体。(2)多家族媒体集:如果数据库管理员将多个备份文件将数据库备份分布到多个文

件中去,称多个文件的整体是“多家族媒体集”。多家族媒体集在以后数据库的备份与还

原操作时,不允许单独对其中的某个文件进行操作,也不允许其他文件加入到这个多媒体

家族中来。

3. 实验内容

(1)为School数据库创建一个完全数据库备份。(2)为School数据库实施一个差异数据库的备份。(3)为School数据库和日志文件进行备份。

Page 141: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

134  数据库系统实验指导教程

4. 实验步骤

(1)通过数据库完全备份可以生成备份完成时刻数据库的一致性快照。

SQLServer在备份开 始 时 记 录 日 志 序 列 号LSN。写 入 日 志 的 每 个 记 录 指 定 一 个

LSN,用于跟踪变化,同时复制数据库的页组。值得指出的是,因为完全备份是一个动态

备份过程,为了保证得到完全一致快照,SQLServer在备份页组时,SQLServer再次记录

LSN,备份第一个LSN和最后一个LSN之间的日志部分,将它添加到备份中。为了节省

备份时间,完全备份放在数据库活动较少的时间备份。

数据库的完全备份,可以有两种不同的方式进行。

一种方式是通过企业管理器完成,步骤如下:

① 从【开始】|【程 序】|MicrosoftSQLServer|【企 业 管 理 器】进 入 到SQLServer

2000的图形化界面进行系统、数据库的管理和维护。进入到【企业管理器】初始窗口后,

通过鼠标不断地单击左边窗口中的 + 展开各层目录,在【控制台根目录】下的第4层目录

为【数据库】,如图5.1所示。

图5.1 SQLServer企业管理器的数据库视图

② 展开“数据库”文件夹,右键单击数据库,指向“所有任务”子菜单,然后单击“备份

数据库”命令,出现如图5.2所示的对话框。

③ 在对话框“常规”标签页面,可以进行如下操作:

在“名称”文本框内,输入备份集名称(如School备份)。在“描述”文本框中输入对备

份集的描述。(可选)

在“备份”选项区域中,根据需要选择数据库备份的类型,在这里选择“数据库完全”

单选按钮,进行School数据库的完全备份,如图5.2所示。在“目的”选项区域中,单击“磁带”或“磁盘”单选按钮,然后指定备份目的地。如果没

Page 142: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

135  第5章 数据库的备份与还原

图5.2 SQLServer备份的“常规”标签页

图5.3 SQLServer“备份设备”的选择

出现备份目的地,则单击“添加”以添加现有的

目的地或创建新目的地,也可以是前面创建的

备份设备。如图5.3所示。在“重 写”选 项 下,如 果 选 择“追 加 到 媒

体”,则数据库备份将追加到备 份 设 备 上 任 何

现有的备份中。如果选择“重写现有媒体”,则数据库备份将重写备份设备中 任 何 现 有 的 备

份。用户可以根据自己的需要任意选择。在“调度”复选框调 度 备 份 操 作 在 以 后 执

行或定期执行,如图5.4所示,SQLServer提供了四种调度类型,可以结合数据库的使用

情况,合理选择。

图5.4 SQLServer备份任务的调度类型

Page 143: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

136  数据库系统实验指导教程

④ 单击“选项”标签,看到如图5.5所示的画面,可以设置在备份完成后是否验证备

份,如果选择“完成后验证备份”复选框,在备份时对备份进行验证。如果选择“备份后弹

出磁带”复选框,在备份操作完成后弹出磁带。该选项只适用于磁带设备。选择“检查媒

体集名称和备份集到期时间”,检查备份媒体以防意外重写。在“媒体集名称”框中,输入

将用于备份操作的媒体的名称。如果仅指定备份集到期时间,则将其保留为空。

图5.5 SQLServer备份的“选项”检签页

图5.6 SQLServer的备份进程

⑤ 当完成以上完全备份选项设置后,单击确定,将

看到数据库的备份过程,如图5.6所示。另 一 种 方 式 的 数 据 库 的 完 全 备 份 可 以 利 用

TransactSQL完成,其语法形式为:

犅犃犆犓犝犘犇犃犜犃犅犃犛犈犱犪狋犪犫犪狊犲_狀犪犿犲犜犗{犇犐犛犓|犜犃犘犈}='狀犪犿犲'

其中,参 数database_name指 定 要 备 份 的 数 据 库,TO

DISK|TAPE说明备份到磁盘或磁带,name指定备份使

用的物理文件名或物理设备名。

例如,为School数据库执行完全备份到前面创建的备份设备。

犫犪犮犽狌狆犱犪狋犪犫犪狊犲犛犮犺狅狅犾狋狅狊犮犺狅狅犾狊狋狅狉犲

(2)数据库差异备份。

数据库差异备份是备份自上次数据库完全备份以来改变的页组。它具有较完全备份

速度快,占用空间较小优点。但是为了利用数据库差异备份来正确恢复数据库,数据库的

完整备份是必要的前提。假设在周一对数据库School进行了完全备份,以后每天晚上进

Page 144: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

137  第5章 数据库的备份与还原

行一次差异备份。当数据库在星期六发生故障后,则只需要恢复数据库的完全备份和最

后一个正确的差异备份,就可以将数据库恢复到最近的状态。

利用企业管理器进行数据库差异备份的步骤和完全备份差不多,在对话框“常规”页

面的数据库备份类型,选择“数据库差异”备份即可。

同样也可以使用TransactSQL语言完成数据库的差异备份。

其语法为:

犅犃犆犓犝犘犇犃犜犃犅犃犛犈犱犪狋犪犫犪狊犲_狀犪犿犲犜犗{犇犐犛犓|犜犃犘犈}='狀犪犿犲'犠犐犜犎犇犐犉犉犈犚犈犖犜犐犃犔

例如,为School数据库执行差异备份(也称为增量备份)到前面创建的备份设备。

犫犪犮犽狌狆犱犪狋犪犫犪狊犲犛犮犺狅狅犾狋狅狊犮犺狅狅犾狊狋狅狉犲狑犻狋犺犇犐犉犉犈犚犈犖犜犐犃犔

(3)数据库事务日志备份。数据库事务日志备份用于复制数据 库 事 务 日 志 中 的 事 务,然 后 删 除 活 动 部 分 以 外

的所有日志,释放空间。事 务 日 志 是 上 次 日 志 备 份 以 来 所 有 事 务 的 顺 序 记 录,利 用 事

务日志可以将数据库 恢 复 到 出 故 障 的 时 刻。备 份 日 志 后,可 以 将 事 务 日 志 截 尾,从 日

志中清除非活动事务,为 新 事 务 腾 出 空 间,防 止 日 志 填 满 或 在 日 志 设 置 为 自 动 扩 大 文

件时变得太大。用企业管理器进行数据库差异备份的步骤和完全备份差不多,在对话框“常规”页面

的数据库备份类型,选择“数据库事务日志”备份即可。也可以使用TransactSQL语言完成数据库事务日志备份。其语法是:

犅犃犆犓犝犘犔犗犌犱犪狋犪犫犪狊犲_狀犪犿犲犜犗{犇犐犛犓|犜犃犘犈}='狀犪犿犲'

例如,为School数据库执行日志备份到前面创建的备份设备。

犅犃犆犓犝犘犔犗犌犛犮犺狅狅犾犜犗狊犮犺狅狅犾狊狋狅狉犲

注意:简单恢复模型不允许备份日志文件。(4)文件与文件组备份。文件与文件组是针对超大数据库设计的特性。如果一个数据库由六个大的数 据 文

件组成,使用文件 或 者 文 件 组 备 份,则 可 以 在 星 期 一 备 份 文 件 一,星 期 二 备 份 文 件 二

等。这样可以在更小的窗口进行备份,从而 减 少 数 据 库 的 离 线 时 间。当 数 据 库 出 现 故

障时,只需要恢复损坏 的 文 件 即 可,这 样 就 可 以 节 省 大 量 用 于 数 据 库 维 护 的 时 间。要

利用备份文件或文件 组 进 行 数 据 库 的 恢 复,还 需 要 保 证 数 据 库 文 件 之 间 的 同 步,因 此

还需要日志备份,利用出故障部分的日志备 份 文 件,结 合 备 份 的 文 件,可 以 使 数 据 库 得

到很好的恢复。用企业管理器进行数据库差异备份的步骤和完全备份差不多,在对话框“常规”页面

的数据库备份类型,选择“文件文件组”备份即可。文件备份和恢复操作必须与事务日志备份一起使用,因此,文件备份也不适用于简单

恢复模型。其语法为:

Page 145: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

138  数据库系统实验指导教程

犅犃犆犓犝犘犇犃犜犃犅犃犛犈犱犪狋犪犫犪狊犲_狀犪犿犲{犉犐犔犈=犳犻犾犲_犾犻狊狋|犉犐犔犚犌犚犗犝犘=犳犻犾犲犵狉狅狌狆_犾犻狊狋}

5. 习题

作为数据库管理员,如何为School数据库制定一个完整的数据库备份计划?并讨论

一下具体的实施方案。

实验5.2 SQLServer数据库的还原

1. 实验目的

  数据库恢复是数据库系统发生故障后,数据库管理员或用户利用先前建立的数据库

的备份,将数据库从故障中恢复过来的过程。

2. 原理解析

SQLServer有三种恢复模型,分别是简单恢复模型、完全恢复模型和大容量日志记

录恢复模型。

SQLServer在恢复之前需要验证存在的数据库备份,并且执行某些使得用户能够启

动恢复进程的特定任务。

3. 实验内容

学会改变设置数据库的恢复模型;学会验证备份文件;掌握数据库还原的策略与方

法;备份与恢复中不一致问题解决的办法。

4. 实验步骤

1)设置数据库的恢复模型

利用企业管理器来设置恢复模型,步骤如下:(1)从【开始】|【程 序】|MicrosoftSQLServer|【企 业 管 理 器】进 入 到SQLServer

2000的图形化界面进行系统、数据库的管理和维护。进入【企业管理器】初始窗口后,通

过鼠标不断地单击左边窗口中的“+”展开各层目录,在【控制台根目录】下的第4层目录

(如图5.7所示)为【数据库】。(2)展开“数据库”文件夹,右击数据库,指向“属性”子菜单,对弹出的对话框中选择

“选项”页面,如 图5.7所 示。在 故 障 还 原 模 型 选 项 卡 中,可 以 设 置 故 障 还 原 的 模 型 为

“完全”。

利用 TransactSQL设 置 数 据 库 恢 复 模 型 以 及 使 用 ALTERDATABASE语 句 的

RECOVERY子句设置恢复模型都是可以的。例如,将School数据库设置为完全恢复模型。

犃犔犜犈犚犇犃犜犃犅犃犛犈犛犮犺狅狅犾犛犈犜犚犈犆犗犞犈犚犢犉犝犔犔

Page 146: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

139  第5章 数据库的备份与还原

图5.7 SQLServer数据库恢复模型的设定

2)恢复前确保备份设备中文件的正确性

在恢复之前,要确保备份设备中文件的正确性,并且其中包括期望的备份。可以通过

企业管理器,查看各个备份设备的属性。但为了得到更多的备份文件的 信 息,需 要 使 用

TransactSQL来检查。

具体的语句包括:

RESTOREHEADONLY语句———返回备份文件或备份集的头部信息。

RESTOREFILELISTONLY语句———返回原始数据库和日志的有关信息。

RESTORELABELONLY语句———返回备份媒体的有关信息。

RESTOREVERIFYONLY语句———验证备份集是否完整以及是否可读。

在备份设备属性 页 面 上 单 击 查 看 内 容,就 可 以 看 到 备 份 设 备 中 的 信 息。如 图5.8所示。

图5.8 SQLServer备份设备的属性查看

Page 147: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

140  数据库系统实验指导教程

同样,执行语句:

狉犲狊狋狅狉犲犺犲犪犱犲狉狅狀犾狔犳狉狅犿狊犮犺狅狅犾狊狋狅狉犲

将会得到以下信息:

备份文件或备份集的名称和描述。

所有备份媒体的类型,如磁盘、磁带。

采用的备份方法,如完全、差异、事务日志还是文件和文件组的备份。

执行备份的日期和时间。

备份的尺寸。

特定备份在备份文件链中的序号。详细情况参见SQLServer联机丛书。执行语句:

狉犲狊狋狅狉犲犉犐犔犈犔犐犛犜犗犖犔犢犳狉狅犿狊犮犺狅狅犾狊狋狅狉犲

得到信息如图5.9所示。

图5.9 利用TSQL查看数据库文件的信息

3)数据库的还原方法

需要根据不同的备份策略方式,选择不同的还原方法。具体来说有下面几种:

(1)从完全数据库备份中恢复。

当数据库系统处于以下情况的时候,可以选择从完全数据库备份中恢复数据库。

存储数据库的物理磁盘受到破坏。

整个数据库受到损坏、崩溃或删除。

数据库的相同副本正在被恢复到相同的其他SQLServer。

当用户选择从完全备份中恢复的时候,SQLServer将重新创建数据库及其相关的文

件,并将它们放置在原来的位置。

注意:在进行数据库完全恢复的时候,如果在实施完全数据库备份的时候,没有事务

日志或差异备份文件,就应当选择RECOVERY选项初始化还原进程。如果存在事务日

志或差异备份文件,可以利用NORECOVERY选项推迟还原进程,直到恢复最近一次备

份为止。

利用企业管理器进行数据库的恢复,步骤如下:

① 展开“数据库”文件夹,右击数据库,指向“所有任务”子菜单,然后单击“还原数据

库命令”。出现如图5.10所示的对话框,在“常规”标签页中,进行如下设置:

在“还原为数据库”文本框中,选择要还原的数据库。

选择要还数据库、文件或文件组还是从设备还原。

Page 148: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

141  第5章 数据库的备份与还原

图5.10 SQLServer数据库还原的“常规”标签页

在“显示数据库备份”中选择选定数据库的备份。

在“要还原的第一个备份集”中指定要还原的备份。只有在还原数据库时才显示

这个选项。

如果指定“时点还原”,则可以选定从即时点还原备份。

② 在“选项”标签页中,可以进行如图5.11所示的设置。

图5.11 SQLServer数据库还原的“选项”标签页

Page 149: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

142  数据库系统实验指导教程

图5.12 SQLServer数据库还原进程

③ 单击确定按钮,完成数据库的恢复,将显

示恢复过程如图5.12所示。(2)也可以利用TransactSQL语句来进行

数据库的还原工作。

① 根据数据库全备份进行恢复。任何磁盘故障引起的数据库混乱或崩溃,都

首先需要利用数据库全备份进行恢复,然后再进

行增量恢复和日志恢复。

犚犈犛犜犗犚犈犇犃犜犃犅犃犛犈犱犪狋犪犫犪狊犲_狀犪犿犲犉犚犗犕{犇犐犛犓|犜犃犘犈}='狀犪犿犲'

[犠犐犜犎[犖犗犚犈犆犗犞犈犚犢|犚犈犆犗犞犈犚犢][犚犈犘犔犃犆犈]]

其中,参数NORECOVERY指示恢复操作不撤销备份中任何未提交的事务,RECOVERY指示恢复操作撤销备份中任何未提交的事务,REPLACE指示即使存在另一个具有相同

名称的数据库,也创建指定的数据库及其相关文件,即删除现有数据库。注意:在数据库恢复之后就使用数据库,应选用RECOVERY。例如,利用先前建立的数据库完全备份,进行数据库恢复。

犚犈犛犜犗犚犈犇犃犜犃犅犃犛犈犛犮犺狅狅犾犉犚犗犕犛犮犺狅狅犾犛狋狅狉犲犠犐犜犎犖犗犚犈犆犗犞犈犚犢

② 根据增量备份进行恢复。在简单恢复模型和完全恢复模型中都可以选择增量备份,增量恢复数据库的命令也

是RESTOREDATABASE,但在根据增量备份进行恢复之前需注意:

已指定NORECOVERY使用RESTOREDATABASE命令完成了全备份恢复。

在进行增量恢复时根据需要指定RECOVERY或NORECOVERY子句。

若有多个增量备份,则一定要按备份的先后顺序进行恢复。当用户选择从差异备份中恢复的时候,SQLServer只恢复数据库中自最近一次完全

数据库备份以来发生的更改部分,从而将数据库恢复到执行差异备份时所处的状态。注意:在进行差异恢复的时候,首先应该将数据库采用完全备份进行恢复。如果还存在事务日志备份的 时 候,在 恢 复 时 指 定 NORECOVERY选 项,如 果 没 有,

指定RECOVERY选项。

③ 根据事务日志进行恢复。采用事务日志备份进行数据库恢复,SQLServer将只恢复事务日志中所做的事务更

改。通过事务日志恢复,可以将数据库恢复到某个特定的时间点。利用事务日志进行恢复之前需注意:

已完成了全备份恢复或增量备份恢复。

若有多个日志备份,则一定要按备份的先后顺序进行恢复。

   犚犈犛犜犗犚犈犔犗犌犱犪狋犪犫犪狊犲_狀犪犿犲犉犚犗犕{犇犐犛犓|犜犃犘犈}='狀犪犿犲'[犠犐犜犎[犖犗犚犈犆犗犞犈犚犢|犚犈犆犗犞犈犚犢][犛犜犗犘犃犜=犱犪狋犲_狋犻犿犲|犛犜犗犘犃犜犕犃犚犓='犿犪狉犽_狀犪犿犲[犃犉犜犈犚犱犪狋犲狋犻犿犲]

|犛犜犗犘犅犈犉犗犚犈犕犃犚犓='犿犪狉犽_狀犪犿犲'[犃犉犜犈犚犱犪狋犲狋犻犿犲]]

其中,STOPAT、STOPATMARK指定将数据库恢复到指定日期时间、指定标记的状态,

Page 150: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

143  第5章 数据库的备份与还原

所有中间恢复都用NORECOVERY,最后一个事务反之。例如,恢复到指定标记的实验。

④ 根据文件或文件组备份进行恢复。

犚犈犛犜犗犚犈犇犃犜犃犅犃犛犈犱犪狋犪犫犪狊犲_狀犪犿犲{犉犐犔犈=犳犻犾犲_犾犻狊狋|犉犐犔犚犌犚犗犝犘=犳犻犾犲犵狉狅狌狆_犾犻狊狋}

犉犚犗犕{犇犐犛犓|犜犃犘犈}='狀犪犿犲'

注意:当使用文件或文件组备份进行恢复时,最后一个文件或文件组恢复操作完成

后,必须将事务日志应用于数据库文件,使之与数据库的其余部分保持一致。如果被恢复

的文件自上次备份后没有做过任何修改操作,则不必应用事务日志。

5. 习题

(1)作为数据库管理员,针对在实验5.1的实验步骤中提出的School数据库方案,假设数据库在星期三发生了故障,应该采取怎样的恢复措施?

(2)用户尝试设计应用程序来备份与恢复School数据库。

实验5.3 系统数据库的备份与恢复

1. 实验目的

  读者通过对系 统 数 据 库 的 备 份 与 还 原,深 入 了 解 系 统 数 据 库 Master、Model、Msdb的作用,并制定系统数据库的备份与恢复策略,以保证数据库系统的可靠性与正确性。

2. 原理解析

(1)SQLServer系统数据库的介绍。

Master数据库从整体上控制用户数据库和SQLServer操作,在创建了任何用户定

义的对象后,都要备份它。

Model数据库为新数据库提供模板和原型。

Msdb数据库包含了有关作业、报警及操作员等信息。

Tempdb数据库也是系统数据库,但是由于它每次在SQLServer停止以后,Tempdb数据库的所有数据都会被全部清除。因此它是唯一不需要备份的系统数据库。

(2)SQLServer系统数据库,特别是Master数据库,在出现以下情况以后需要备份。

① 当增加或删除用户数据库。但是如果增加或删除文件或文件组,或用户数据库自

动增加来容纳新添加的数据,这些操作并不对 Master数据库产生影响,所以此时不必对

其进行备份;

② 当创建新的登录或执行与登录有关的操作,但是增加数据库用户并不影响Master数据库;

③ 当创建或删除备份设备;

④ 当为了进行分布式查询或远程过程调用而对数据库服务器进行配置,如增加连接

服务器或远程登录等。

Page 151: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

144  数据库系统实验指导教程

(3)在大多数情况下,备份系统数据库与用户数据库的方法一样。但在恢复时,就应

该考虑不同情况采用不同的策略。如果是在数据库没有受到破坏的情况下,恢复系统数

据库和用户数据库的恢复策略相同。但是如果在包含系统数据库的媒体受到破坏时,可

能就不得不重建数据库系统。

3. 实验内容

(1)从已有的数据库备份中还原系统数据库。(2)当系统受到破坏时,重建系统数据库。(3)当系统受到破坏时,附加或还原用户数据库。

4. 实验步骤

(1)系统数据库是数据库管理员重点保护的对象,在一般的情况下,会在数据库管理

系统发生变化时,备份系统数据库文件。这种情况下,在SQLServer服务能够启动的情

况下,用户直接使用企业管理器或TransactSQL语言,利用存储的系统数据库备份直接

恢复系统数据库,这种方法和一般用户数据库的还原没有区别。(2)重建系统数据库发生在Master数据库受到损坏、且用户也无法启动SQLServer

服务的情况下。重建的步骤如下(在重建之间,最好对系统数据库重新进行完全备份,以

便于重建后,将系统数据库恢复到最新的状态):

① 在系统“服务”管 理 中,双 击 服 务 选 项,停 止 MSSQLSERVER服 务

(见图5.13),将数据库文件目录下的 Master数据文件与日志文件剪切或删除,人为制造

系统数据库的破坏。

图5.13 在系统“服务”管理中停止 MSSQLSERVER服务

② 利 用 SQLServer安 装 目 录\MicrosoftSQLServer\80\Tools\Binn\下 的

rebuildm.exe来重建系统数据库 Master,重建 Master是整个数据库服务器重建的开始。

在DOS方式下,运行rebuildm.exe,看到如图5.14所示的界面。在“包含数据文件的源

目录”中,从SQLServer安装盘 上 找 到 Master数 据 文 件 所 在 的DATA目 录。单 击“重

建”,数据库系统就开始重建系统数据库。系统重建完毕后,Master、Model与 Msdb数据

库返回到安 装 时SQLServer时 的 状 态。系 统 重 建 完 毕 后 的 图 形 界 面 显 示 如 图5.15所示。

Page 152: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

145  第5章 数据库的备份与还原

图5.14 使用rebuildm.exe重建系统数据库

图5.15 使用rebuildm.exe重建系统数据库成功提示

③ 重新启动SQLServer服务,在“控制面板”中,选择“管理工具”,单击“服务”项,在

服务中启动 MSSQLServer服务即中。然后就可以打开SQLServer企业管理器,做进

一步的数据库管理。

④ 恢复系统数据库的备份。为了使数据库尽可能恢复到最近的状态,数据库管理员

应该利用日常的备份文件来还原数据库。

(3)附加或恢复用户数据库。

假若某个时候没有 Master数据库的备份,也并不意味着SQLServer数据库管理系

统失去功能,管理员可以使用系统存储过程sp_attach_db或sp_attch_sigle_file_db来还

原用户数据库,通过执行系统存储过程向 Master数据库报告用户数据库的信息。

Page 153: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

146  数据库系统实验指导教程

   狌狊犲犿犪狊狋犲狉犲狓犲犮狊狆_犪狋狋犪犮犺_犱犫犱犫狀犪犿犲='狊犮犺狅狅犾',@犳犻犾犲狀犪犿犲犾='犇:\狊狇犾犱犪狋犪\犛犮犺狅狅犾_犇犪狋犪.犖犇犉'

5. 习题

当开发完成后,程序员经常需要将数据库迁移到用户使用的环境,请编写程序实现这

一操作。

习题答案

实验 5.1

  作为数据库管理员,在制定备份计划时需要作全面考虑,并在实践中调整方案,如可

以为School数据库在每个月开始时做一次完全备份,然后每个星期做一次差异备份,每

天做一个日志备份。

实验 5.2

(1)利用本月初建立的数据库完全备份恢复数据库,然后再利用上一星期的差异数

据库的备份,将数据库恢复到上一星期的状态。然后再利用周一和周二的日志备份,将数

据库恢复到星期二的状态。(2)本书提供一个用于实现数据库完全备份与恢复的应用程序供读者参考。源代码

采用Delphi7.0设计,位于sourcecode的数据库备份与还原。

实验 5.3

见源代码文件中数据库附加子文件夹,将数据库数据文件.mdf和 日 志 文 件.ldf,通

过执行存储过程sp_attach_db来附加到用户的数据库系统。

Page 154: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

性 能 监 测

C H A P T E R 6

第6章

性能问题是数据库应用中的最为令人关注的高级话题。作 为 一 个 现

实数据库系统的数据库管理员,其工作的大部分就是围绕着性能展开的,同时,这也决定 了 性 能 是 一 个 庞 大 的 话 题。与 数 据 库 系 统 性 能 相 关 的 部

分,由硬件设备的投入和配置、数据库管理系统软件的选择,延伸到具体操

作中的技巧,由数据库需求分析、设计开始,延伸到应用代码的编写、调试,因此需要数据库管理人员通过丰富的知识积累和经验来驾驭。

本章目的主要是介绍如何利用数据库管理系统提供的工具,对以事务

为单位的操作性能进行监测,比如CPU时间、内存使用数、磁盘I/O次数

等。数据库性能问题的其他丰富内容,将在其他章节进行讨论。本章实验

的内容主要是通过对一系列针对 MicrosoftSQLServer2000数据库管理

系统的性能监测工具的介绍,使读者能够运用这些工具对数据库应用的性

能进行跟踪监测,并能根据监测结果分析对比各个事务安排的合理性,了

解数据库应用中的I/O瓶颈。在经过本章若干性能监测工具的学习之后,希望读者可以掌握数据库性能这一庞大的知识体系的基本技术,并尝试应

用到现实应用中去。

【事务经历】

在数据库中,事务是业务应用运行的逻辑单位,它作为一个 原 子 操 作

单位不可分割地存在,要么事务中的全部操作都执行,要么全部操作都不

执行。因此,在数据库中对性能进行考察的时候,也可以将事务作为观 察

单位。事实上,以下的许多分析,都将围绕事务的经历展开。本章的介绍从事务对数据库的读写操作开始入手。当谈到 读 写 操 作

时,是指最原始意义上的操作;而面向集合的SQL语句正是由这些操作组

成的。“读”是指访问一个数据项,比如数据库表中的某一行或者数据库的

某个索引项;“写”是指对数据库中某个数据项的改变。以下使用记号Ri(A)表示事务Ti对数据项A的一次读操作,其中Ti

表示该事务,i是 数 据 库 系 统 给 出 的 事 务 标 识 号,在 事 务 开 始 被 调 度 时 分

配。类似的,使用记号 Wj(B)表示事务Tj对数据项B的一次写操作。

Page 155: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

148  数据库系统实验指导教程

那么,某两个事务Ti和Tj执行时交错的读写操作可能是这样的:

Ri(A)Wi(A)Rj(B)Ri(B)Wi(B)Wj(B)类似以上的一个操作序列称为事务经历,或者称之为一个调度。

【交错的读写操作】

正如在数据库理论课程中学习到的一样,一组访问相同数据集合的事务的不当调度

可能引发数据库的不一致,这是数据库管理系统要极力避免的情况。对于一个串行经历而言,不会产生由于不同事务的读写操作交错执行而导致的不一

致的问题,因为在串行经历中在一个事务的所有操作完成之前,其他任何事务的操作都不

能执行。那么为什么系统要交错执行来自不同事务的读写操作呢?为什么不简单地将所

有的事务都严格的串行执行呢?首先看看串行执行的具体定义。下面这个方法可以作为

调度器用在操作上实现事务的串行执行的规则:一旦事务Ti执行了一个数据访问操作

(Ri或 Wi)开始该事务,则称该事务Ti是活动的,或者说在执行中。此时如果代表另外

一个用户的事务Tj向调度器发出了一个初始化该事务的操作(Rj或 Wj),调度器将使事

务Tj处于等待状态直到Ti完成。如果有多个事务被强制等待,那么当事务调度器发现

某个事务结束时,将使用某种调度策略选择一个事务,并启动此事务的执行。大多数数据库系统不强制事务严格串行执行的原因很简单:不同事务操作的交错执

行大大地提高了系统的性能。在处理过程中允许多个事务并发执行意味着当一个事务进

行I/O操作时,另外一个事务可以使用CPU,这样就提高了整个系统的吞吐率(吞吐率以

给定时间内完成的事务数量来衡量)。因为I/O操作较费时间,因此当有多个磁盘并允

许同时有多个活动事务时,将大大提高系统的吞吐率。下面通过图形来展示一个事务过程中CPU与I/O操作的协调,来说明并发性对吞

吐率的作用。假设有一组几乎同时开始的事务,其事务经历都是简单的:使用CPU读数

据使用CPU写数据。其 中 每 次 使 用 CPU 都 用5ms,而 读 写 数 据 的I/O操 作 则 需 要

45ms。那么,首先看看一组严格串行调度的事务所产生的操作序列。图6.1中用阶跃图来表示资源使用情况,用一定的间隔来表示使用CPU和磁盘I/O

所用的时间。可以看到,每个成功完成的事务总共需要占用100ms,也就是说每隔100ms完成一个事务,所以吞吐率为每秒10个事务(10TPS)。在这种情况下,CPU未被充分使

用,因为在100ms的时间中只有10ms在使用CPU,也就是说CPU使用率只有10%,说

明CPU在绝大多数时间内处于等待状态,而此时却有其他许多事务在队列中等待开始,这无疑是一种对资源的浪费。但是此时磁盘却得到了很好的使用率(高达90%),这是因

为事务经历中对磁盘进行了充分的使用。

图6.1 严格串行调度的事务操作序列

Page 156: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

149  第6章 性能监测

再来考察同样硬件配置下(一个CPU和一个磁盘的情况),允许事务并发执行 的 情

况,如图6.2所示。图中示出两个CPU和磁盘I/O,是为了示例方便。

图6.2 同样硬件条件下两个事务交错的操作序列

可以看到,在图6.2中,每个事务执行的时间增加了,因为每个事务花费了很多时间

在等待其他事务完成I/O操作,以便进行自身的I/O操作。但是,请注意到这种调度方

案成功地将CPU使用时间和磁盘I/O时间重叠起来。这样,CPU的利用率提高了,而磁

盘则 几 乎 处 于 满 负 荷 运 转 之 下,利 用 率 接 近100%,所 以 总 的 吞 吐 率 可 以 提 高 到

11.11TPS(每两个事务180ms)。可以看到,并发执行可以提高系统的事务吞吐率。在现实的系统中,可能还会配置多个磁盘,来配合一个CPU进行工作,以弥补CPU

和磁盘I/O之间的速度差距。这样,在合理的调配和调度下,可以使不同事务针对不同

外部磁盘进行I/O操作,不互相冲突,而CPU可以因为同时调度了更多的事务而利用率

更高。这样,并发程度更高,系统的事务吞吐率可以更为明显地提高。当然,增加磁盘数

量是需要更多的资金投入的,数据库设计人员往往需要在一定的资金限额下,设计出达到

最优系统性能的配置,来满足用户的需求。

【可串行化】

事务的并发执行可以提高事务吞吐率和资源利用率,但是交错的读写操作却可能带

来各种各样的操作冲突问题。这些问题包括“不可重复读”、“丢失更新”、“写写冲突”等,在理论课程的学习中都对这些问题进行了介绍。

出现了这些冲突,会使得事务进行之后,数据库可能处于一种不一致的状态。这主要

是事务的ACID属性被打破的后果。处于不一致状态的数据,往往就不能够很好地表述

现实中的状态,因此也就失去了数据库的意义。而一系列的事务如果按照串行化执行的标准,就可以保证数据库的一致状态。根据

这样的事实,数据库专家们提出了事务经历的“可串行化”定义:如果一个经历等价于一

个串行经历,这个经历就是可串行化的。也就是说,系统对一系列的事务进行并行调度,而且结果等价于对这一系列事务的某一个串行调度,那么这个调度得到的事务经历就是

可串行化的。

Page 157: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

150  数据库系统实验指导教程

一个事务调度经历是否是可串行化的,是数据库系统常常要考虑的问题。为了对调

度进行检测,人们设计了许多数学化的理论和工具来检测某一个经历是否是可串行化的。其中,最为常用的是前趋图,它记录了隐含在经历中的一些冲突操作决定下的事务之间的

先后顺序。由前趋图,人们提出了判断某事务经历是否是可串行化的充分必要条件,称之

为“可串行性定理”。限于本书篇幅和实验目的,这里不再详述,读者可以参考数据库理论

书籍对此定理进行学习。

【锁机制】

在现实中,数据库常常使用“锁机制”来保证大量事务的调度经历是可串行化的。首先,介绍在商业数据库系统中用来保证事务一致性的一个非常著名的锁机制,称为

两段锁协议,缩写为2PL,它被大多数商业数据库系统所采用。协议最主要的部分定义了

加锁要分为两个不能交叉的阶段:申请阶段和释放阶段,并由此证明了一个重要的加锁

定理,说明遵守2PL原则的事务型操作经历必定是可串行化的。由这个结论了解到,只要系统都用2PL来规范事务加锁的行为,就可以获得并行执

行的效率和串行执行的效果。但是,事实上,2PL协议是一个非常严格的协议,它使到某

一事务往往持有未来一段时候之后才使用的锁,而另一事务却由于等待该锁的释放而处

于等待状态。也就是,遵循2PL协议的事务调度达到了并行执行,但是并发程度很低,资源还是没有充分被利用。而现实中的应用也是丰富多彩的,有需要严格保持数据库一致

性的操作,也有一些只读性的事务,仅仅需要了解数据库的概貌但是要求快速得 到 结 果

(这些事务对一致性的要求往往没有那么严格)。因此,为了更好地对不同要求提供不同的服务来解决这个性能问题,数据库系统提供

了“隔离级别”的 定 义 工 具。SQL99向 用 户 提 供 了 如 下 四 个 隔 离 性 依 次 增 高 的 隔 离 级

别:ReadUncommitted、ReadCommitted、RepeatableRead和Serializable。到了最后一

个隔离级别,事务经历才达到可串行化的水平。而其他的隔离级别,则提供了不同情况下

的性能需求,给用户不同的选择空间。

实验6.1 查看事务的执行计划

1. 实验目的

  本节实验介绍在DBMS上观看事务执行计划的工具,以使读者掌握通过DBMS的工

具来观察某个事务将会怎样被执行的方法。

2. 原理解析

在DBMS中,每个用户利用SQL语言给出的事务都会根据关系代数理论,被分解转

换成查询树和查询图,然后再由查询分析器生成规范的查询计划。在某些时候,需要对事

务的执行计划进行评审,这时,便可利用DBMS的显示执行计划工具。

Page 158: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

151  第6章 性能监测

3. 实验内容

本节主要介绍SQLServer2000中显示事务执行计划的工具,包括对SHOWPLANTEXT|ALL选项以及图形化显示计划工具的介绍,这些工具可以展示某个事务将会怎

样被执行。具体是:

使用SHOWPLAN_TEXT选项。

使用SHOWPLAN_ALL选项。

使用图形化的显示计划。

4. 实验步骤

SQLServer2000提供了一些显示执行计划的选项,根据选择的选 项 提 供 了 各 种 不

同的信息,这也是最为简单的监测工具。下面通过几个小实验来展示这一特性。

1)显示文本执行计划

如果在进行查询时进行了SHOWPLAN_TEXT选项的选择,SQLServer2000会改

变查询结果,数据库管理系统不会真正执行这个事务,而是列出执行这个事务的“计划”,而不会得到真正的结果。实际上,NOEXEC选项(SQLServer中使事务不被真正执行的

选项)被自动包含在SHOWPLAN_TEXT选项中,使事务的“执行”退化成“计划”。打开和关闭SHOWPLAN_TEXT选项的语法非常直接:

犛犈犜犛犎犗犠犘犔犃犖_犜犈犡犜[犗犖|犗犉犉]

使用了SHOWPLAN_TEXT选项之后会得到本次事务的执行计 划。再 次 说 明,由

于NOEXEC选项自动使用,所以客户端不会看到任何执行结果,实际上事务也并没有被

执行。步骤如下:

① 打开SQLServer的查询分析器,获得服务器的一个连接实例。

② 在查询分析器中输入:

犝犛犈犛犮犺狅狅犾犌犗犛犈犜犛犎犗犠犘犔犃犖_犜犈犡犜犗犖犌犗犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犌犗犛犈犜犛犎犗犠犘犔犃犖_犜犈犡犜犗犉犉犌犗

每一个语句后面都有一个GO语句,因此使它成为自己批处理的一部分。由于每个

关于SHOWPLAN_TEXT的语句都必须是一个单独的事务,所以这里的每个GO都是

必需的。

③ 运行这个查询。如果采用“文本显示结果”(在代码输入框空白处单击右键,在弹

出的菜单中可选),那么结果应如图6.3所示。

Page 159: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

152  数据库系统实验指导教程

图6.3 SHOWPLAN_TEXT的结果

结果分析:从结果中,可以看到,针对“SELECTFROMTEACHERS”的查询,查询分析器将

运用在表格生成时默认建立起来的、在TEACHERS表格中主 键 上 的 索 引 来 协 助 查 询,提高性能。

2)显示完整的文本执行计划

SHOWPLAN_ALL选项和SHOWPLAN_TEXT选项的使用十分相似,同样使得事

务仅 仅 是 进 行 “计 划”,而 没 有 实 际 的 执 行,因 此 也 不 会 得 到 执 行 结 果。其 实

SHOWPLAN_ALL选项的 功 能 完 全 覆 盖 了SHOWPLAN_TEXT选 项 的 功 能,使 用 了

SHOWPLAN_ALL选项之 后,会 收 到 使 用SHOWPLAN_TEXT选 项 时 接 收 的 所 有 结

果,同时还会得到一组额外的统计信息,这些信息包括:

计划进行的物理和逻辑操作。

执行这些操作需要的参数以及参数的默认值。

估计会涉及的记录行数。

估计需要的I/O次数。

估计需要的CPU时间。

涉及的记录行的每行平均大小。

计划中每一步输出的结果。

计划的每一步的警告信息。

每一步计划的操作类型。

是否以并行的方式运行事务。

其他一些物理的底层信息,如在执行计划树中某一步操作的节点编号。步骤如下:

① 重新在SQLServer的查询分析器中获得服务器的一个连接实例。

② 在查询分析器中输入:

犝犛犈犛犮犺狅狅犾犌犗犛犈犜犛犎犗犠犘犔犃犖_犃犔犔犗犖犌犗

Page 160: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

153  第6章 性能监测

犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犌犗犛犈犜犛犎犗犠犘犔犃犖_犃犔犔犗犉犉犌犗

③ 运行这个查询。可以在结果显示框中看到,系统根据SHOWPLAN_ALL选项的

要求,显示出了大量的与执行计划相关的信息,如图6.4所示。SHOWPLAN_ALL的结

果不宜以纯文字的格式显示(全部信息大约有800个字符宽度),所以建议读者使用“以表

格显示结果”的选项,利用表格的形式来显示这些信息。结果分析:这些信息包含了有用的CPU时间和I/O次数估算,可以帮助用户在确定不同的执

行方式时发挥作用,如图6.4所示。

图6.4 SHOWPLAN_ALL的结果

如果用户仅仅需要知道基本的执行计划,如是否使用某个索引等,那么就可以仅仅使

用SHOWPLAN_TEXT选项。如果需要利用查询分析器估计出更多的执行成本信息,那么SHOWPLAN_ALL选项就可以派上用场。

这里再通过一个例子,来说明实际中SHOWPLAN_ALL选项的作用。它往往提供

给用户一种以短时间、无副作用(因为事务本身并未真正执行)来获取运行成本估计值的

方法,这正是“计划”所具有的真正优势。这个例子针对实验环境中的STUDENTS表格和CHOICES表格进行,目的是查找

选择了课程号为10010的全部学生的姓名。一种方法是简单地进 行 两 个 表 格 的 连 接 操

作,之后进行条件选择;另一种方法是进行嵌套查询。由数据库理论课程的学习,读者可

以知道表格连接操作将耗费极大的资源,而嵌套将是一种优化的方法。那么,在具体的数

据库操作系统中是如何体现的呢?下面来进行理论和实践的结合。这两个表格总共拥有

几十万条记录,如果真正进行实际的事务,耗费的时间和资源将是相当可观的,这个时候,就可以灵活应用SHOWPLAN_ALL选项。

步骤如下:

① 在查询分析器中获得一个连接。

② 在查询分析器中输入:

犝犛犈犛犮犺狅狅犾

Page 161: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

154  数据库系统实验指导教程

犌犗犛犈犜犛犎犗犠犘犔犃犖_犃犔犔犗犖犌犗犛犈犔犈犆犜狊狀犪犿犲犉犚犗犕犛犜犝犇犈犖犜犛,犆犎犗犐犆犈犛犠犎犈犚犈犮犻犱='10010'

犌犗犛犈犔犈犆犜狊狀犪犿犲犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈狊犻犱犐犖(犛犈犔犈犆犜狊犻犱犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犮犻犱= '10010')

犌犗犛犈犜犛犎犗犠犘犔犃犖_犃犔犔犗犉犉犌犗

结果分析:第一个与商业逻辑相关的事务采用了表格的连接,而第二个则采用了嵌套查询的方

式。运行之后,可以在结果栏中得到计划信息,如图6.5所示,得到的结果是可以令读者

感到满意的,它印证了数据库理论的分析结果。

图6.5 两种方案的查询计划对比

可以看到,两个事务的执行计划是不同的,因此其后的估计统计信息也有不同。其中

对决策选择有着重要影响的几项信息:查询估计涉及的行数和估计CPU时间。在某实

验环境下两种 方 案 的 两 组 数 值 为:5.9668467E+8行 对6238.4121行、2557.4006s对

0.22980799s。如此巨 大 的 性 能 差 异,用 户 自 然 会 作 出 合 理 的 选 择,而 这 将 要 归 功 于

SHOWPLAN_ALL选项的应用。

3)显示图形化的执行计划

图形化显示计划工具组合了大量的SHOWPLAN_ALL选项信息,并将它们封装成

图形显示的格式。这项功能需要通过查询分析器的菜单进行选择,而不是用TSQL的语

法来打开它,所以只有在使用查询分析器的时候才能够使用它了。(1)在不执行事务的情况下显示计划。这是使用“显示估计的执行计划(DisplayEstimatedExecutionPlan)”来达到的。单

击查询分析器的“查询”→“显示估计的执行计划”即可打开此功能,再单击即关闭此功能。注意到这只是估计的执行计划,事务不会被真正执行。

Page 162: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

155  第6章 性能监测

步骤如下:

① 在查询分析器中取得一个连接实例。

② 在查询分析器中输入:

犝犛犈犛犮犺狅狅犾犌犗犛犈犔犈犆犜狊狀犪犿犲犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈狊犻犱犐犖(犛犈犔犈犆犜狊犻犱犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犮犻犱= '10010')

犌犗

③ 打开“显示估计的执行计划”选项。

④ 运行这个查询。可以在结果框中看到与图6.6类似的结果:

图6.6 显示估计的执行计划

结果分析:可以看到,“显示估计的执行计划”功能通过图形的方式,形象直接地展示了事务的执

行计划。还可以看到,图形中列出了各个步骤的预计成本占整个事务成本的百分比,令用

户可以一目了然地得知本事务的瓶颈在哪里。另外,当用户将鼠标移动到某一个代表计

划的一步操作的图标上时,这个图形化的执行计划还将展开该操作的详细信息,其主要内

容和SHOWPLAN_ALL选项类似,但是更为直接易读。(2)在执行事务的同时显示计划。之前的计划都是在事务没有真正执行的情况下进行的估计,自然也可以通过查询分

析器的“显示执行计划(ShowExecutionPlan)”选项,在真正执行事务的同时也显示执行

计划。还是在查询分析器的“查询”→“显示执行计划”即可打开此功能,再单击可以关闭。这个选项与“显示估计的执行计划”是十分类似的,不同的仅仅是事务是否有真正地

被执行。读者可以自己在查询分析器中选择“显示执行计划”并运行上一个例子,可以看

到类似的图形化的计划,不同的是还可以看到真正执行后得到的结果表格。最后再说明一点,这种图形化的显示令用户更加容易接受和阅读,但是其缺点除了无

法用TSQL直接控制开关(因此只能在查询分析器中使用)之外,还有这种方式不能输出

成文本报表形式进行保存。

Page 163: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

156  数据库系统实验指导教程

5. 习题

(1)本实验中提到NOEXEC选项,尝试利用和使用SHOWPLAN_TEXT的方法类

似的方式来开启和关闭这个选项。

(2)本实验介绍的“图形化显示计划”工具具有丰富的信息,前面的介绍部分只是列

出了简单的叙述。请读者对一个多步骤的事务运用这个工具,并仔细查看预测的统计信

息,分析这些统计信息的意义。

实验6.2 查看事务执行的统计信息

1. 实验目的

  本实验介绍在DBMS上观看事务执行统计信息的工具,以使读者掌握通过DBMS的

工具来获取某个事务具体执行信息的方法。

2. 原理解析

数据库管理员(DBA)在日常的应用中,常常需要监测数据库系统乃至整个计算机系

统的运行状态。于是,DBA希望获得关于执行事务的统计信息的报告。许多成功的商业

DBMS都提供了丰富的事务信息统计和展示工具。这些工具通过在事务执 行 期 间 对 事

务的各方面参数属性进行记录,以便在事务执行结束后得到对事务性质的总体把握。

本实验得到的统计数据中,会有一些较为迷惑的属性名称,特别是在I/O信息的部

分。这里首先对这些概念进行如下的介绍和分析。

① 物理读(PhysicalReads):这代表了从磁盘上读取实际物理页的次数。它通常不

会多于而会少于另一项统计信息“逻辑读”的次数。因为当系统进行了一次物理页面的读

取之后,在内存缓冲区中便留下了该页面的内容,加之系统可能启动预读机制,将被访问

的页面附近的页面也读入内存缓冲区(系统认为这些邻近页面也可能很快被访问)以节省

I/O次数提高效率,所以当下一次访问这些页面的时候就不必进行物理读,而仅仅进行逻

辑读。关于物理读信息要注意的另外一点是,连续两次运行同一事务,得到的物理读次数

可能是不同的。原因也是和内存缓冲区相关,由于第一次运行该事务已经将需要的信息

从磁盘读入内存缓冲区,所以第二次运行该事务时就不需要进行物理读取了。有些数据

库管理员在机器刚一启动就运行一些常用的查询,使到内存缓冲区中布满与数据库相关

的缓冲记录,这样可以使到真正的用户进行查询的时候,不必进行物理读取而仅仅是进行

逻辑读取,这样可以减少I/O次数提高效率。

② 逻辑读(LogicalReads):代表实际读取页面的次数。也就是说,如果事务利用了

存在于内存缓冲区中的页面,就会产生一次逻辑读。如果多次需要访问一个页面,会产生

多个逻辑读。

③ 预读(ReadAheadReads):是系统为了提高效率而进行的对物理页面的预读操

Page 164: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

157  第6章 性能监测

作,系统会根据一些统计信息预计某些页面可能即将被访问,因此为了节省多次I/O,系

统会将这些页面一次性读入内存缓冲区中。这些预读的页面是不一定被访问的。

④ 扫描计数(ScanCount):代表访问表格的次数。

⑤ 连接属性(ConnectionProperty):数据库用户使用查询分析器或其他接口登录到

数据库服务器进行数据库事务操作,可以认为是建立了一个连接。这个连接有许多数据

库应用层的选项可以供用户选择,例如之前介绍的 NOEXEC其实就是建立的连接的属

性的一种。

如果得到的统计信息中物理读的次数很多,说明进行了多次的磁盘I/O操作,这可

以通过像上面所说的预先进行一次事务,使到内存缓冲区中充满相关记录来改进。

然而如果逻辑读的次数很多,那么应该考虑为事务涉及的表格添加索引。因为逻辑

读次数很高,说明事务需要在涉及的记录中频繁地移动,此时恰当的索引往往可以令记录

的定位更加快速,因而可以减少不必要的移动,从而控制逻辑读的次数。

3. 实验内容

本实验 主 要 介 绍SQLServer2000中 显 示 事 务 执 行 统 计 信 息 的 工 具,包 括 对IO、

CPU、DBMS服务端和客户端统计信息查看工具的介绍,这些工具可以展示某个事务具

体是怎样被执行的。具体是:

使用STATISTICSIO选项。

使用STATISTICSTIME选项。

使用连接属性面板。

显示服务端跟踪信息。

显示客户统计信息。

4. 实验步骤

上面所进行的性能评估都是基于“计划”的,也就是一种估计值。除了使用查询分析

器的显示计划功能之外,用户还可以利用查询分析器的功能获取事务执行时“真实的”信

息,来评估事务执行时的真实性能。这些信息可以通过设置STATISTICS选项来获取,

其用法与SHOWPLAN_ALL类似。STATISTICS实际上包含了一组选项,在解决事务

性能问题时,这些选项非常方便。

下面通过几个小实验来展示这一特性。

1)显示事务执行的IO统计信息

这是一个指出在哪里和怎样进行事务的非常常用的工具。

STATISTICSIO选项提供了事务执行时需要考虑的关键信息,这些信息包括:

物理读(PhysicalReads)。

逻辑读(LogicalReads)。

预读(ReadAheadReads)。

扫描计数(ScanCount)。

Page 165: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

158  数据库系统实验指导教程

正如之前介绍的一样,通过控制这些信息中过高的值,可以优化事务的IO性能。

步骤如下:

① 打开SQLServer的查询分析器,获得服务器的一个连接实例。

② 通过在查询分析器中输入如下的代码来进行实验(假设STUDENTS表格中存在

sname为gfxrgs的记录):

犝犛犈犛犮犺狅狅犾犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犔犈犆犜犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈狊狀犪犿犲='犵犳狓狉犵狊'

犌犗犛犈犔犈犆犜犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈狊狀犪犿犲='犵犳狓狉犵狊'

犌犗犆犚犈犃犜犈犐犖犇犈犡狊狋狌犱犲狀狋_狀犪犿犲犗犖犛犜犝犇犈犖犜犛(狊狀犪犿犲)

犛犈犔犈犆犜犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈狊狀犪犿犲='犵犳狓狉犵狊'

犇犚犗犘犐犖犇犈犡犛犜犝犇犈犖犜犛.狊狋狌犱犲狀狋_狀犪犿犲犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犉犉犌犗

③ 运行这个查询。结果可以在查询分析器消息栏中看到,如图6.7所示。

图6.7 STATISTICSIO信息的获取

结果分析:实验中共有三个核心事务,前两个事 务 是 一 样 的,都 是 在STUDENTS表 格 中 找 到

名字为“gfxrgs”的记录。在这里,安排连续两次 执 行 相 同 的 事 务 的 原 因 是,让 读 者 看 到

两次执行中内存缓冲区的变化。从图6.7中 的 前 两 行 统 计 信 息 可 以 看 到,这 两 个 事 务

的逻辑读次数是一样的,但是物理读和预读却是第一次的2次和1488次对第二次的两

个0次。原因正是上文分析的,通过第一个事务的执行,内存缓冲区充满了与此次事务

逻辑相关的信息,所以 第 二 次 执 行 相 同 事 务 时,对 页 面 的 访 问 都 只 需 在 内 存 缓 冲 区 中

取得即可。当然,这样 的 情 况 的 出 现 是 必 须 用 足 够 数 量 的 内 存 来 保 证 的。另 外,还 可

以看到系统预读功能的强大,它发生了1488次 预 读,其 中 绝 大 多 数 是 预 测 正 确 的 有 效

Page 166: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

159  第6章 性能监测

预读,这使得即使是 第 一 次 运 行 事 务,也 会 控 制I/O的 数 量 在 一 定 范 围 内。还 可 以 看

到,这个事务的逻辑 读 次 数 非 常 多,达 到 将 近1500次。正 如 上 面 分 析 的 一 样,此 时 可

以考虑在适当的表 格 列 上 建 立 索 引。这 也 正 是 第 三 个 事 务 设 计 出 来 的 初 衷。由 于 所

查询的谓词是姓名sname,所以,为了提高 速 度,用 户 应 该 在STUDENTS表 格 的sname字段上建立一个索引。上面的第三个事务正 是 由 此 开 始,之 后 再 进 行 与 前 面 两 个 事 务

一样的SELECT逻辑。所 看 到 结 果 的 统 计 信 息 说 明 这 个 性 能 调 整 方 案 达 到 了 目 的。建立索引的操作的耗费与执行之前的事务的 耗 费 是 类 似 的,但 是 当 这 个 索 引 被 建 立 好

了之后,以后每次的事务的逻辑读次数将可 以 大 大 减 少,这 里 减 少 到6次,性 能 上 有 了

几个数量级的飞跃。最 后,事 务 将 索 引 删 除,主 要 是 为 了 之 后 可 以 没 有 冲 突 地 使 用 这

个例子。

2)显示事务执行的CPU统计信息

这是一个非常简单但是实用方便的选项。它直接显示了CPU实际执行事务所用的

时间,而不需要自己去计算。相信有了之前的经验,读者可以猜测到它的使用方法。步骤如下:

① 打开SQLServer的查询分析器,获得服务器的一个连接实例。

② 在查询分析器中输入:

犝犛犈犛犮犺狅狅犾犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犛犈犔犈犆犜犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈狊狀犪犿犲='犵犳狓狉犵狊'

犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犉犉犌犗

③ 运行这个查询。结果如图6.8所示。

图6.8 STATISTICSIO信息的获取

结果分析:可以看到,STATISTICSTIME选项非常直接明了地展示出事务执行过程中的CPU

Page 167: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

160  数据库系统实验指导教程

统计信息。

3)连接属性

上面介绍了许多有用的选项,可以通过TSQL提供的语法对其进行开关设置。这些

选项非常实用,但是通过TSQL语法对其进行设置,一开一关显得较为繁复。查询分析

器简化了这一切。其思想是将这些选项作为本次在查询分析器中获得的连接实 例 的 属

性,用户可以在“连接属性”面板里面设置这些属性的开关,其作用即等价于一个开关对应

选项的 TSQL语 句。进 行 了 这 样 的 设 置 之 后,在 此 连 接 实 例 中 进 行 的 事 务,都 会 带 有

(开)或不带有(关)这些选项的相应输出。这种可视化的手段,使得选项的应用更加方便

快捷,适合普通用户掌握。

步骤如下:

① 在查询分析器的“查询”菜单下,单击“当前连接属性”菜单项,可以打开“当前连接

属性”面板。

② 从 面 板 中 可 以 看 到 前 面 介 绍 过 的 SHOWPLAN_TEXT、STATISTICSIO、

STATISTICSTIME等选项,在这些选项前的方框中打上勾,就可以开启此选项,而没有

勾即是处于关闭状态。

结果分析:

界面如图6.9所示。

图6.9 连接属性的设置

可以看到,连接属性中还有许多其他选项,例如前面提到过的NOEXEC选项,这些

也可以通过打勾的方 式 进 行 设 置,读 者 可 以 自 行 试 验,这 里 不 再 一 一 详 述。注 意 到 其

中的一些选项功能是 互 斥 或 是 包 含 的,当 用 户 选 中 其 中 某 一 个 选 项 时,与 其 互 斥 或 包

含的选项会变成暗 淡 的 颜 色 并 成 为 不 可 选 择 的,从 而 保 证 了 连 接 属 性 本 身 不 会 出 现

矛盾。

Page 168: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

161  第6章 性能监测

4)显示服务器跟踪

在查询分析器中,还有“显示服务器跟踪”的功能,显示一个事务执行 中 的 服 务 端 信

息。在查询分析器的“查询”菜单中单击一次“显示服务器跟踪”,即可打开此选项,再单击

一次即关闭。下面使用一个简单的例子来展示这一功能。步骤如下:

① 打开SQLServer的查询分析器,获得服务器的一个连接实例。

② 在查询分析器中输入:

犝犛犈犛犮犺狅狅犾犌犗犛犈犔犈犆犜犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈犵狉犪犱犲犻狊犖犝犔犔犌犗

③ 运行这个查询。输出结果可以在查询分析器的结果窗格中的“跟踪”一栏中获取。结果分析:如图6.10所示。可以看到,这个跟踪服务为每个语句提供一行的统计信息,包括持

续时间、CPU时间、读写次数等,展示出在服务端的一组信息。这里的服务端应当看成是

数据库管理系统,而所谓的客户端,就是查询分析器本身。

图6.10 显示服务器跟踪

5)显示客户统计

除了服务端信息外,查 询 分 析 器 还 可 以 跟 踪 客 户 端 即 查 询 分 析 器 在 事 务 中 的 统 计

信息。

使用查询分析器的“查询”菜单中的“显示客户统计”选项可打开此功能。

① 打开SQLServer的查询分析器,获得服务器的一个连接实例。

② 在查询分析器中输入:

犝犛犈犛犮犺狅狅犾

犌犗

犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈狊犪犾犪狉狔犻狊犖犝犔犔

犌犗

③ 运行这个查询。输出结果如图6.11所示。

结果分析:

从结果中可以获取许多客户端与服务端交互的统计信息,如应用程序的配置统计信

息、网络统计信息、时间统计信息等。

Page 169: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

162  数据库系统实验指导教程

图6.11 显示客户统计

5. 习题

(1)运用本章学习的获取“真实的”统计信息的工具,证实之前学习的“显示计划”工

具对性能估值的正确性。(2)通过本章的介绍,可以看到“显示客户统计”工具显示了相当多的信息。尝试使

用不同配置的事务、并在不同的网络环境下测试这个工具,比较统计信息的不同。(3)读者可以在“连接属性”中看到一些上面介绍过的选项,但是还有更多没有直接

介绍的选项,请读者切实了解其中选项的作用,注意其中一些选项的互斥关系。必要时,可以参照SQLServer的连接丛书。

实验6.3 运用查询调控器

1. 实验目的

  本实验将介绍SQLServer2000上的一个设置查询事务执行时间上限的工具,使读

者更好地体会到,数据库管理人员是如何通过DBMS提供的一系列接口对数据库的事务

操作进行监控的。

2. 原理解析

正如前面所说的,在DBMS中,每个用户利用SQL语言给出的事务都会根据关系代

数理论,被分 解 转 换 成 查 询 树 和 查 询 图,然 后 再 由 查 询 分 析 器 生 成 规 范 的 查 询 计 划。

DBMS可以通过这个计划估计出执行时间,并与用户设置的时间上限进行 比 较,拒 绝 执

行超过时间限制的查询事务,以此来保证对数据库事务执行时间的控制。

Page 170: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

163  第6章 性能监测

3. 实验内容

本实验主要介绍SQLServer2000中的查询调控器工具。在介绍了许多查询分析器中

的工具之后,这里把注意力投向SQLServer2000的企业管理器(EnterpriseManager)。本实验介绍的查询调控器是内嵌在企业管理器中的一个工具,提供了接口给数据库

管理人员设置查询事务执行的上限,预计执行时间超过这个上限的事务将不会 被 执 行。这是SQLServer2000中提供的一个保证事务吞吐率、事务响应速度、事务调度公平性的

一个简单但是实用的工具。

4. 实验步骤

在企业管理器中,针对每个服务器,都有一个“属性”面板,当服务器运行的时候,这个

面板是可以打开的。面板中的“服务器设置”页提供了界面,给用户设置“查询控制器”的

数值。打开企业管理器,在目标服务器的图标上单击右键,在弹出的菜单中选择“属性”,即可弹出“SQLServer属性(配置)”面板,选中其中的“服务器设置”页,即可以看到“使用

查询调控器防止超出指定成本的查询”项,如图6.12所示。

图6.12 查询调控器设置

可以在查询调控器后的文本框中输入数值,表示超过这个秒数的查询将被中断(数值

的单位是秒)。因此,从理论上说,如果设置了数值50,那么就表示不允许执行估计运行

时间超过50秒的查询。这在阻止系统运行一些长时间的查询时非常方便。这里用了“理论上”这个词,原因是进行比较的时间值只是一些估计值,而不是现实中

的运行时间,因此,在速度不同的机器上,会产生不同的误差。这个属性的设置还有一定的技巧。当服务器打开时,这个值设置之后并不会立即生

Page 171: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

164  数据库系统实验指导教程

效,要在停止服务器之后,重新启动后才会起作用。在面板上的最下方还可以看到“配置

值”和“运行值”两个互斥选项,“配置值”是默认选项,在这种状态下,系统开放给用户对服

务器参数进行配置,并在重新启动服务器之后生效;而选中“运行值”选项,面板上会显示

另外一套数值,并且数值都是只读的,其含义是服务器本次运行的参数。这个面板的其他

页面也有类似的特点,用户可以在其中配置其他实用的数据库服务器特性。下面使用一个简单的例子来展示这一功能。步骤如下:

① 打开SQLServer的查询分析器,获得服务器的一个连接实例。

② 将“查询调控器”的时间设置成50秒。

③ 在查询分析器中输入:

犝犛犈犛犮犺狅狅犾犌犗犛犈犔犈犆犜狊狀犪犿犲犉犚犗犕犛犜犝犇犈犖犜犛,犆犎犗犐犆犈犛犠犎犈犚犈犮犻犱='10001'

犌犗

④ 在具有几十万条记录的数据库上运行实验,可以看到如图6.13的失败提示信息。

图6.13 预计时间开销超过时限的事务被取消

结果分析:

可以看到系统列出系统的预计开销,并判断出它超过了时限,所以将此事务取消。

5. 习题

请读者自行设计一个运行时间预计较长的事务,然后在查询调控器中设置和不设置

事务运行成本上限时间的不同情况下,运行此事务,体会查询调控器在阻止超过预期成本

的事务中发挥的作用。

实验6.4 运用事件探查器

1. 实验目的

  在数据库的应用中,数据库管理员有的时候需要实时地了解数据库系统各方面的情

况,然而手动的编写脚本一般来说既复杂又不全面,不够灵活且实时性也很难达到。鉴于

这种需求的普遍性,DBMS中有了集成一种实时监控工具的需求。

本实验介绍在DBMS上实时观察事务执行状况的工具,以提供一种更强的方式让读

者了解DBMS上事务执行的情况。

Page 172: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

165  第6章 性能监测

2. 原理解析

在用户访问数据库服务器的时候,是作为一个客户端连接上 服 务 器 来 进 行 操 作 的。

伴随着连接,服务器处理本地或网络上的数据库操作请求。数据库服务器对来自各个连

接的服务请求是清楚的,并将这些行为写入日志中。

现在,为了提供给数据库管理员一种实时监控DBMS的手段,SQLServer2000要求

管理员建立一个连接到服务器的连接,服务器将当前的数据库操作中管理员感兴趣的一

部分(通过定制来刻画感兴趣的部分)通过连接反馈给用户。当然,要求这个连接具有足

够的权限。这一切,可以利用SQLServer2000的“事件探查器”来进行。

3. 实验内容

在SQLServer2000中,有一个功能非常强大的 工 具“事 件 探 查 器”,微 软 将 其 作 为

一个单独的工具,和“企业管理器”、“查询 分 析 器”等 并 列 起 来,足 以 见 其 重 要 性。实 际

上,它也是数据库管理 员 非 常 常 用 的 监 测 工 具。在SQLServer6.5中 有 一 个 被 称 为

“SQL跟踪器”的工具,而事件 探 查 器 功 能 在 跟 踪 器 上 升 级 了 许 多,但 是 从 它 们 都 是 可

以实时跟踪“探测”出哪些事件正在服务器 上 运 行 的 意 义 上 说,这 两 个 工 具 具 有 一 定 的

继承性。

本实验介绍使用这个工具,从中体会数据库管理员对数据库系统的实时监控的需求

和采用的方式。

SQLServer2000的“事件探查器”是一个较为庞大和独立的工具,这里首 先 对 其 进

行介绍。

要启动“事件探查器”,可以在 Windows“开始”菜单“所有程 序”列 表 中 的 MicrosoftSQLServer项中打开,也可以在“企业管理器”的工具菜单下单击打开。打开之后,可以

看到“事件探查器”的界面非常简单,但是使用起来却是非常富有变化。

在其“文件”菜单下的“新建”项中选择“跟踪”,这意味着用户需要新建一个对服务器

的“跟踪”。探查器非常自然地弹出一个窗口,需要用户确认目标服务器,并填写安全信息

登录到服务器上。这与“查询分析器”的使用类似,登录之后这台服务器就是用户的“跟

踪”目标。请注意,为了进行跟踪,必须使用sa等具有SystemAdministrators服务器角

色的成 员 登 录。否 则 会 出 现 用 户 登 录 失 败 信 息,原 因 是“未 与 信 任SQLServer连 接 相

关联”。

成功登录之后,可以看到如图6.14的“跟踪属性”设置面板“常规”标签页。

在“常规”标签页中,“跟踪名”和“跟踪SQLServer”这两个名字很容易理解,而第二

组关于模板的信息可能比较令人费解。模板是事务探查器中预先定义好的要在跟踪中查

看的事件、数据信息和在跟踪中要过滤掉不显示的信息,简而言之,就是定义了用户要跟

踪什么、不跟踪什么。用户可以不使用固定的模板而利用这个面板的后面几页内容进行

自我设置,也可以简单地利用SQLServer提供的模板,针对不同情况下最可能需要跟踪

的信息进行查看跟踪。模板的命名也是按照 需 要 跟 踪 某 类 信 息 的 环 境 来 进 行 的,模 板

Page 173: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

166  数据库系统实验指导教程

图6.14 跟踪属性的“常规”标签页

文件存放在SQLServer安装目录下的80\Tools\Templates\SQLProfiler文件夹中,用

户可以通过“模板名”的下拉列表框进行选择。

下面的两个选项是定义把捕获的信 息 保 存 到 磁 盘 的 文 件 中、或 是 保 存 在 数 据 库 的

表格中、或是不保存而 仅 仅 在 事 务 探 查 器 的 屏 幕 上 显 示(信 息 在 关 闭 此 跟 踪 后 则 会 消

失)。如果将信息保存在文件中,那么只有通过文件所在的文件系统进 行 访 问;而 如 果

将信息保存在数据表中,那么其他用户可以通 过 连 接 到 服 务 器 并 查 询 数 据 库 来 访 问 这

些信息。

最后的一项信息设置是定义随后自动关闭跟踪的时间。

在进行了常规的基本设置后,用户可以进入事件页进行跟踪属性的设置。这个页面

包含了更多重要的设置信息。

如图6.15所示,这个页面有左右两边两个大的树型目录,提供给用户定制“需要跟踪

目标服务器上的××类型的××事件”的机会。左边的“可用事件类”是事件探查器可以

提供的跟踪事件类型(而用户目前并未选择该事件类型),而左边的“选定的事件类”是用

户目前定制的要跟踪的事件类型,初始时是从定义的模板中读取的。用户可以从左边的

窗口中展开树型目录,看看事件探查器提供了哪些可能的事件可以跟踪,并可以以一类事

件或单个事件的形式“添加”到右边的树型目录中来(添加后左边的事件类型会消失,出现

在右边)。相反地,用户也可以通过“删除”按钮撤销已经选择过的事件类型。单击某一个

事件,还可以在面板下部的文本框中看到针对这种事件的介绍,用户可以利用这个功能对

可以跟踪的各种事件进行了解。

通过这个标签页的设置,跟踪人员可以关注到某一些真正关心的事件上。

Page 174: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

167  第6章 性能监测

图6.15 跟踪属性的“事件”标签页

下一个标签页“数据列”如图6.16所示。这一页的构造和上一页是类似的,也分为左

右两个窗体。上一个标 签 页 提 供 了 给 用 户 定 制“需 要 跟 踪 目 标 服 务 器 上 的××类 型 的

××事件”的机会,而这个标签页则提供了给用户定制“需要跟踪目标服务器上的选定跟

踪事件的××信息”的机会。体现在最终的跟踪信息上,上一页定义了要跟踪的行,本页

定义了要跟踪的列。

图6.16 跟踪属性的“数据列”标签页

Page 175: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

168  数据库系统实验指导教程

在这里,有一个常见的错误是将关于一个事件的所有信息全部选择,认为这样可以获

得最完整的信息,其中必定会有需要的信息,而最终没有派上用场的数据不去关 注 就 是

了。但其实最好不要这样做,除非用户真的有需要观看全部的 跟 踪 信 息。这 是 因 为,首

先,全部数据列的定制意味着“数据库服务器”和“事件探查器”不得不从通信管道上交换

许多实际上并没有派上用场的附加文本信息,由于这两者的通信是进程之间的(甚至是基

于网络的),所以这意味着当探查器运行时,系统会有很大的负担———即跟踪信息越多,负担越大。其次,这必然同时意味着效率的低下———交换了大量的数据,而其实并不需要其

中的大部分数据。这里列出默认值中一些数据列的意义,以供参考:

文本(TextData):这是事件探查器及时添加到跟踪中的语句的实际文本。

应用程序名(ApplicationName):一个重要的事件信息,说明访问数据库的应用程

序名称,这个名称是客户端创建到服务器的连接时设置的。例如对于之前常用的

查询分析器,其应用程序名就是“SQL查询分析器”;如果是使用JDBC或 ADO连接数据库的应用程序,那么就会显示该应用程序名称。当用户试图在系统中定

位事件源的时候,这个选项可以提供重要的信息。

WindowsNT用户名(NTUserName):操作系统的哪个用户触发的事件。

登录用户名(LoginName):说明用哪台服务器上哪个账号的用户触发的事件。

CPU:实际使用CPU时间。

持续事件(Duration):事件持续的时间。

SPID(SQLProcessID):SQL进程ID。如果用户根据跟踪信息判断应该中止掉

某一个进程,那么这个ID就是进程的唯一性标志。最后,看看最后一个标签页,筛选标签页,如图6.17所示。

图6.17 跟踪属性的“筛选”标签页

Page 176: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

169  第6章 性能监测

这个标签页提供用户定制哪些范围的事件需要被跟踪显示,而哪些不用。利用筛选

标签页,用户可以选择只监视ApplicationName为“××”的应用程序触发的事件,可以选

择只监视CPU时间超过“××ms”的事件,可以选择不监视NTUserName为“××”的用

户触发的事件,等等。

4. 实验步骤

通过上面的介绍,读者对事件探查器有了一个概貌的了解,接着,通过实验来对其进

行使用。步骤如下:

① 新建一个跟踪。

② 在新建筛选页中的“Duration(持续时间)”中的“等于或大于”设置“0”。

③ 打开SQLServer的查询分析器,获得服务器的一个连接实例。

④ 在查询分析器中输入:

犝犛犈犛犮犺狅狅犾犌犗犛犈犔犈犆犜犉犚犗犕犜犈犃犆犎犈犚犛犠犎犈犚犈狊犪犾犪狉狔犻狊犖犝犔犔犌犗

⑤ 运行这个查询。可以在事件探查器的跟踪画面中看到类似如图6.18的情景:

图6.18 事件探查器跟踪画面

⑥ 暂停这个跟踪。

⑦ 然后在事件探查器的“文件”菜单中选择“属性”,可以看到如跟踪建立时的“跟踪

属性”面板。在“筛选”页中将Duration修改为1000。

⑧ 然后再在查询分析器中运行上面的例子。

⑨ 回到事件探查器,发现事件探查器中并没有跟踪痕迹。结果分析:第一次运行,可以看到事件探查器根据用户的需求记录并显 示 了 对 数 据 库 的 跟 踪。

然而第二次没有跟踪痕迹,正是实验设置的结果。这是因为,这个事件由于持续时间不满

足要求而被事件探查器过滤掉了。注意,修改跟踪属性,至少需要此跟踪暂停下来。事件探查器是一个非常重要的工具,在解决性能问题和其他问题上有着非常重要的

作用。当用户认为某个事件已经结束,但是却在事件探查器中寻找它的某个分支时,便可

以赶快将其中止;当用户不能确定哪个查询导致了性能急剧下降时,可以在查询分析器

中找到蛛丝马迹时,便可以及时调整策略。查询分析器非常灵活,并提供 了 许 多 定 制 功

能,是一个可以解决实际问题的强大性能监测工具。

Page 177: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

170  数据库系统实验指导教程

5. 习题

(1)“事件探查器”是个非常重要的性能监测工具。本章最后也将它应用在锁信息的

获取上,作为“事件探查器”的一个应用实例。请读者也自己动手建立一些跟踪,针对数据

库系统的TSQL运行事件、TSQL的存储过程运行等事件,然后再参考一下系统自带的

模板,看看建立的跟踪中设置的信息是否符合应用要求,并从模板中进一步学习跟踪的建

立技巧。(2)当今还有 很 多 成 功 的 商 业 数 据 库 产 品,读 者 可 以 了 解 并 尝 试 列 举 出 它 们 中 与

SQLServer2000的“事件探查器”功能类似的工具。

实验6.5 运用性能监测器

1. 实验目的

  在某些情况下,操 作 系 统 也 可 能 对 数 据 库 性 能 参 数 的 监 控 提 供 帮 助。本 实 验 介 绍

Windows平台下针对SQLServer的工具。

2. 原理解析

Windows操作系统和SQLServer系列数据库服务软件都是源自软件巨头Microsoft公司。处于商业上的一些考虑,以及本公司内部一些技术实现的可能性,Windows操作

系统提供了对本公司自行研发的SQLServer服务软件的支持。

3. 实验内容

当在 Microsoft的 WindowsNT系 列 或 Windows2000系 列 操 作 系 统 上 安 装SQLServer并启动数据库服务器时,在操作系统的“性能”监测器工具中,给用户提供了添加许

多关于SQLServer信息的计数器。这些计算器信息其实是关于SQLServer的跟踪统计

信息。性能监测器是操作系统提供的一个优秀工具,可以查找定位乃至解决性能问题,也是包括数据库管理员在内的 Windows高级用户常使用的性能工具。

本实验结合SQLServer2000学习对这个工具的使用。

4. 实验步骤

本实验介绍“性能”监视器的使用方式。

步骤如下:

① 打开“性能”监视器。在 WindowsXP中,有多种可行的方式:

用户可以在以上系列的操作系统的开始菜单下打开控制面板,然后在“管理工具

(AdministrativeTools)”文件夹中找到“性能”监视器快捷方式,双击打开;

也可以到操作系统安装目录下的system32目录下双击perfmon.msc来打开;

或者在开始菜单的“运行”处直接输入perfmon.msc。

Page 178: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

171  第6章 性能监测

② 进入性能监视器。进入系统后,可以看到如图6.19所示的画面。

图6.19 性能监视器界面

③ 观察并熟悉性能监测器的界面。性能监视器的主体分为左右两个部分。左边是控制台的部分,右边的显示界面会随

着控制台的选择而改变。这里最为关注的界面就是“系统监视器”界面,它提供了性能监

视的最形象画面体现,也是默认打开性能监视器后看到的界面,如图6.19所示。“系统监视器”的右边界面的最上方是一组控制按钮,有可以控制中间部分显示效果

的工具栏按钮,用于定制采用不同的视图观察计数器数据(如图表形式、直方图形式、报表

形式等),还突出显示某一计数器的图形效果;还有添加或删除计数器的按钮。界面的最

下方是目前进行监视的计数器内容,以及每种计数器对应于中间界面图形中的哪种颜色

图6.20 “添加计数器”对话框

的曲线。而“占地面积”最大的中间部 分 就 是

计数器监测内容的直观图形显示,随着红色的

垂线沿着时间轴推进。

④ 在“性能”监视器的右边界面上点击右

键,然后单击“添加计数器”菜单项,或 者 直 接

单击右边界面上方的工具栏中的“+”号按钮,系统 便 会 弹 出 “添 加 计 数 器”对 话 框,如

图6.20所示。用户可以在这里选择计数器的来源,而更

为关键的是,可以在此对话框中选择需要监测

的“性能对象”。展开“性能对象”下拉菜单,可以看到许多性能对象,其中有 关 于 处 理 器 的、

Page 179: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

172  数据库系统实验指导教程

关于网络的、关于磁盘的,等等。

还有一组以MSSQL开头的性能对象,这正是 Windows特意添加的针对SQLServer的性能对象。用户可以选定其中的某个,下方关于这个性能对象的所有“计数器”将会显

示在左边的列表框中,而关于某个计数器如果还有更为详细的“范例”可选择,会在右边的

列表框中出现。如果需要帮助,可以单击“说明”查看关于某个计数器的参考。选定计数

器之后,单击“添加”就可以在“性能监视器”界面下方看到添加的计数器,相应的图形也会

在界面中间开始画出。

SQLServer具有的许多不同的性能对象,又对应了一系列的计数器对象。

这里列出几个应该给予重视的性能对象下的计数器:

SQLServer高速缓存管理(CacheManager)下的缓存区命中率计数器(BufferHit

CacheRadio):统计的是SQLServer的 缓 存 的 命 中 率,这 个 比 率 的 计 算 方 法 是

“查找缓存时命中的缓存块个数”比上“查找的个数”。这个比率说明了缓存管理

的效率,告诉管理员目前缓存的性能情况。

SQLServer常 用 统 计 信 息(GeneralStatistics)下 的 用 户 连 接 计 数 器(User

Connections):记录系统中当前活跃的用户连接数。

SQLServer内 存 管 理(MemoryManager)下 的 服 务 器 总 内 存 数(TotalServer

Memory):说明当前服务器使用的整个动态内存数。当用户发现,该数量与系统

的可用内存量接近时,说明内存已经被占用了许多,需要通过添加内存等措施进

行性能的维护。

SQL Server 统 计 信 息 (SQL Statistics)下 的 SQL 编 译/秒 计 数 器 (SQL

Complications/sec):表明SQLServer频繁编译语句的速度。注意,该数字也包

括了重新编译的数量(根据索引统计信息变化或者因为显示需要有时需要对SQL语句进行重新编译)。当服务器开始启动时,该数值可能相对较大,当服务器已经

在常用设置上运行时,该数值趋于稳定。

SQLServer缓存管理器(BufferManager)下的页读/秒(Pagereads/sec)和页写/

秒(Pagewrites/sec)计数器:分别记录服务器从磁盘上进行物理读和物理写的次

数。这里最好是保持在一个低数值水平。

可以混合使用各种计数器,从而能得到服务器当前性能状况的最好了解,并进行适当

的调整。

5. 习题

Windows操作系统自带的“性能”监视器中提供了许多针对SQLServer的性能对象

和计数器。由于数量众多,掌握起来有一定困难,本章中针对这一特点列出了几个需要重

视的性能对象和计数器。请读者实践在“性能”监视器中强调的针对SQLServer的性能

对象及计数器,观察其数据曲线并分析曲线波动的原因。

Page 180: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

173  第6章 性能监测

实验6.6 运用锁监测工具

1. 实验目的

  数据库的锁机制与数据库性能有着内在的联系,本实验介绍利用DBMS的工具对数

据库事务操作的加锁解锁操作进行监测。力图使读者对数据库事务的加锁解锁操作的重

要性有更进一步的认识,并体会数据库事务操作过程中的加锁解锁过程。

2. 原理解析

在DBMS的事务调度中,为了保证并行执行的快速的特点,同时保证串行执行的正

确性,锁机制是一个至关重要的核心机制。通过对不同的事务加锁,并且划分不同的隔离

等级、执行两段封锁协议,可以达到一个良好的运行机制。本实验利用锁 监 测 工 具 来 对

DBMS的加锁解锁过程进行观察。

3. 实验内容

主要介绍SQLServer2000中的锁监测工具,来观察DBMS中的加锁状况。具体是

使用以下工具:

使用存储过程sp_lock。

使用企业管理器。

使用事件探查器。

4. 实验步骤

正如原理解析部分所介绍的,数据库的锁机制与数据库性能有着内在的联系。为数

据库添加隔离程度高的锁,可以保证一致性,但是性能因此下降;反之,性能提高了,但是

面临着一致性被破坏的危险。而且,加锁不当,还有可能造成死锁;解除死锁一般采用中

止某个事务的做法,对资源的浪费更大。因此,考虑性能问题,往往需要考虑数据库的加

锁机制。在本实验中,将介绍一些数据库锁的监测工具。

1)使用存储过程

使用存储过程sp_lock,可以得到系统中哪些锁处于激活状态的信息。为了使用sp_lock存储过程,进行实验。步骤如下:

① 在查询分析器中打开两个不同的查询窗口。

② 在第一个查询 窗 口 中,建 立 一 个 运 行 时 间 相 对 较 长 的 查 询 事 务,这 个 事 务 是 在

STUDENTS表格中查询最大年级的一批学生的信息。例如可以输入:

犝犛犈犛犮犺狅狅犾

Page 181: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

174  数据库系统实验指导教程

犌犗犛犈犔犈犆犜狊犻犱,狊狀犪犿犲,犵狉犪犱犲犉犚犗犕犛犜犝犇犈犖犜犛犪狊狊1犠犎犈犚犈犵狉犪犱犲犻狊狀狅狋犖犝犔犔

     犃犖犇

   犖犗犜犈犡犐犛犜犛

     (犛犈犔犈犆犜     犉犚犗犕犛犜犝犇犈犖犜犛犪狊狊2

     犠犎犈犚犈狊1.犵狉犪犱犲<狊2.犵狉犪犱犲犃犖犇狊1.狊狀犪犿犲<>狊2.狊狀犪犿犲

     )

犌犗

③ 在第二 个 查 询 窗 口 中,建 立sp_lock存 储 过 程(通 过EXEC关 键 字 来 建 立 并 运

行)。输入:

犈犡犈犆狊狆_犾狅犮犽犌犗

sp_lock后面还可以添加进程ID号,也 就 是SPID。要 表 达 多 个 进 程ID号,可 以 用

“,”号来分隔。例如,如果只监视进程ID号为53的加锁情况,可以用“EXECsp_lock53”

的语句来表达。

④ 请确保两个窗口同时可见。

⑤ 执行第一个窗口中的查询操作。

⑥ 在第一个查询事务仍然在执行时,快速切换到第二个窗口,并且执行第二个事务,

即在第一个查询事务过程中运行存储过程sp_lock。

实际上,这里选择运行的第一个窗口中的事务,在具有十万条记录的STUDENTS表

格上的操作时间相当可观,读者可以有充足的时间进行动作。

请关注的是第二个查询窗口的结果,也就是sp_lock的显示信息。而对于第一个查

询窗口中的事务,这里甚至可以在sp_lock事务结束后中止掉它。

sp_lock的显示信息如图6.21所示。

图6.21 sp_lock的显示信息

Page 182: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

175  第6章 性能监测

结果分析:下面对sp_lock结果集的意义进行解释:

spid:SQLServer进程ID号。

dbid:请求锁的数据库标识号。

Objid:请求锁的对象的对象标识号。

IndId:索引标识号。

Type:锁的类型。

Resource:资源的文本描述。当锁类型为PG或EXT时,表示fileid:page组合;当锁类型为RID时,表示fileid:page:rid组合。

Mode:锁请求者的锁模式。

Status:锁的请求状态。其中,锁的类型说明对什么对象进行加锁,包括:

DB:对整个数据库进行加锁。

FIL:对数据文件进行加锁。

IDX:对整个索引进行加锁。

PG:8千字节(KB)的数据页或索引页。

KEY:索引中的行锁。用于保护可串行化事务的键范围。

TAB:对包括所有数据和索引在内的整个表进行加锁。

EXT:对相邻的八个数据页或索引页构成的一组进行加锁。

RID:行标识符。用于单独锁定表中的一行。而锁的请求状态包括:

GRANT:锁是已经被获取的。

WAIT:正在另一个进程中被阻塞。

CNVRT:正转换为另一个锁。锁会保持在一种模式中,但等待获取更强的锁模式。最为重要的锁模式说明了锁与

锁之间的兼容性。一些模式之间是互斥的(这意味着它们不能在一起工作),是否可以一

起工作,取决于它们是否兼容(compatiable)。这些锁模式包括:

共享锁(S锁)。共享锁(SharedLocks)是最基本的一类锁,用在只需要读取数据

的时候(只读的情况下)。共享锁可以阻止“脏读”的情况发生。

排他锁(X锁)。排 他 锁(ExclusiveLocks)也 是 基 本 锁 的 一 种,与 其 字 面 意 思 一

样,排 他 锁 与 其 他 锁 都 是 不 兼 容 的,是 最 强 的 一 种 锁。因 此,排 他 锁 也 称 为 独

占锁。

更新锁(U锁)。更新锁(UpdateLocks)是共享锁和排他锁的混合体。更新锁是

一种特殊的占位符。为了进行更新,需要验证 WHERE条件从句来 确 定 要 更 新

的行,此时只需要共享锁,因为这本质上是只读操作。而到了实际需要进行物理

更新时,就需要加上排他锁。

意向锁(I锁)。意向锁(IntentLocks)主要是为了处理对象层次结构的问题。假

设某一事务在一行上建立了锁,那么此时应当避免其他事务在该行所在页或表格

Page 183: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

176  数据库系统实验指导教程

上建立一个锁。意向锁就是为了避免这样的情况而提出的。意向锁还可以细分

为意向共享锁(IntentSharedLocks,IS锁)、意向排他锁(IntentExclusiveLocks,

IX锁)、共享意向排他锁(SharedwithIntentExclusiveLocks,SIX锁)。

架构锁(Sch锁)。架 构 锁(SchemaLocks)包 括 两 种 风 格:架 构 修 改 锁(SchemaModificationLocks,SchM 锁)和 架 构 稳 定 性 锁(SchemaStabilityLocks,SchS锁)。SchM锁用于在对象上改变框架,而SchS主要是为了阻止SchM锁。

大容量更新锁(BU锁)。大容量更新锁(BulkUpdateLocks)实际就是表锁的变

体,但是又与表锁存在一定的差别。大容量更新锁允许并行加载数据,也就是说,该表对普 通 的 TSQL语 句 是 上 锁 的(不 兼 容 的),但 是 允 许 多 个 大 容 量 插 入

(BULKINSERT)操作同时进行。下面使用表6.1来表示锁模式之间的兼容性。YES表示兼容,也就是两个这样模式

的锁可以一起工作,而NO表示不兼容。

表6.1 各种锁模式之间的兼容性

锁 模 式 IS S U IX SIX X

意向共享锁(IS) YES YES YES YES YES NO

共享锁(S) YES YES YES NO NO NO

更新锁(U) YES YES NO NO NO NO

意向排他锁(IX) YES NO NO YES NO NO

共享意向排他锁(SIX) YES NO NO NO NO NO

排他锁(X) NO NO NO NO NO NO

此外,还包括:

除了SchM锁以外,SchS锁与其他所有的锁兼容。

SchM锁与所有锁模式都不兼容。

BU锁只与SchS锁以及其他的BU锁兼容。通过存储过程sp_lock,就可以获取这些关于锁机制的丰富信息。

2)使用企业管理器

但是,使用sp_lock存储过程获取到的信息,没有直接体现出哪些对象上确实有这些

对象,而只提供了dbid和ObjId,用户必须使用这些信息在数据库 的 系 统 表 格 中 获 取 信

息。例如利用ObjId在sysobjects表格中获取对应的对象名称,利用dbid在master数据

库中的sysdatabases表格中获取对应的数据库名称。也可以利用如下的查询:

犛犈犔犈犆犜犗犅犑犈犆犜_犖犃犕犈(犗犅犼犐犱)

犛犈犔犈犆犜犇犅_犖犃犕犈(犱犫犻犱)

分别获取对应对象名称或数据库名称。另外,使用存储过程sp_lock较为麻烦,需要使用两个窗口来同时运行。因此,为了

提供给 用 户 更 直 观 更 方 便 操 作 的 图 形 化 界 面,在SQLServer的 企 业 管 理 器 中,集 成 了

sp_lock存储过程的功能。这就是企业管理器的“当前活动”信息功能。

Page 184: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

177  第6章 性能监测

企业管理器的“当前活动”提供了两种不同的方式显示锁,也就是通过进程ID或通过

对象两种方式。要使用企业管理器显示锁,只要在企业管理器的控制台根目录上到达“管理”目录下

的“当前活动”节点,如图6.22所示。

图6.22 企业管理器的“当前活动”信息

只要展开用户感兴趣的节点(按照进程或对象),就可以看到各种锁。显示锁的各项

信息都已经在上一部分介绍,这里只是采取更加直观的方式显示出来,并直接加入了各种

信息,避免用户自己的查找。这个工具的最大优势是,当在查看某一个锁的信息时,可以双击这条锁信息或在其上

单击右键选择“属性”,就会显示一个对话框,告诉用户此进程最后运行的语句,并有一组

与进程所在主机简单通信、取消进程等功能的按钮。在处理死锁时,这是一个非常方便的

工具。

3)使用事件探查器

上面进行的对锁的信息获取,都是一种手工的跟进,对执行历时很短的事务,可能难

以捕捉。所以可 以 利 用 前 面 介 绍 过 的“事 件 探 查 器”建 立 跟 踪 实 时 监 控 某 些 事 务 的 锁

信息。步骤如下:

① 依照前面的介绍,新建一个“跟踪”。

② 建立到目标服务器的连接。

③ 然后在“跟踪属性”面板的“常规”标签页按照需要进行设置。

④ 接着,来到“事件”标签页,在“选定的事件类”中删除默认情况下要监视的事件类,而从“可用事件类”一边添加“锁”事件类型下的一些事件(读者可以参看对事件的说明,按照需要选择),其中最为重要的有Lock:Acquired(锁的获取)和Lock:Released(锁的释

放)两个事件。

Page 185: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

178  数据库系统实验指导教程

⑤ 然后再进入“数 据 列”标 签 页,选 择 一 些 需 要 的 数 据 项,例 如EventClass、SPID、

ApplicationName、LoginName等等。

⑥ 这里“筛选”标签页不做额外设置。

⑦ 运行这个跟踪。

⑧ 为了试验跟踪的效果,读者可以打开“查询分析器”,建立一个连接后,在查询窗口

中运行一些TSQL语句代码。

⑨ 切换到“事件探查器”的窗口,就可以从中看到类似图6.23的滚动信息。

图6.23 事件探查器的锁信息跟踪

结果分析:这组信息会随着跟踪事件的进行而不断实时跟进,用户还可以根据需要选择对结果

进行保存、添加其他的“数据列”以满足实际需要。这也可以说是“事件探查器”的一个实

际应用,其灵活性在此充分体现出来。

5. 习题

锁机制始终是数据库系统一个非常重要的问题。请读者利用锁信息监测工具,对本

章中出现的例子中的锁信息进行各方面的监测,并根据这些信息联系理论分析锁机制的

原理和在SQLServer2000中的现实应用。

实验6.7 综合应用

1. 实验目的

  通过一个综合实验,对前面介绍的工具进行一个综合应用。

2. 原理解析

对前面的理论和技术的综合应用和验证。

Page 186: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

179  第6章 性能监测

3. 实验内容

在本实验中,通过一个例子,来对之前学习的性能监测工具做一个综合性的应用。考

虑这样一个情景:在一次全校的统一考试之后,老师们经过繁忙的改卷和成绩录入工作之后,连同之前

的学生、教师和课程信息,得到了拥有相当大数据量的School数据库(其实这就是本章一

直使用的实用平台)。现在,需要在这些录入信息的基础上,进行一系列的统计处理,来分

析此次考试的状况,如:高分(分数大于90分)人数、缺考(分数为空值)人数、高分学生的

信息等。下面,将首先提出初步方案,然后根据数据库性能调试的原则对方案进行改进,最后

提出最终方案。

4. 实验步骤

1)初步的方案

假设某用户开始着手进行统计处理,那么从直观的阶段入手,编写出如下可以达到功

能的SQL代码:

犝犛犈犛犮犺狅狅犾犌犗犛犈犔犈犆犜犮狅狌狀狋()犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈狊犮狅狉犲>=90犛犈犔犈犆犜犮狅狌狀狋()犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈狊犮狅狉犲犻狊犖犝犔犔犛犈犔犈犆犜狊.狊犻犱,狊.狊狀犪犿犲犉犚犗犕犆犎犗犐犆犈犛犪狊犮,犛犜犝犇犈犖犜犛犪狊狊犠犎犈犚犈犮.狊犮狅狉犲>=98犪狀犱犮.狊犻犱=狊.狊犻犱犌犗

这个执行代码可以获得高分人数、缺考人数、高分学生的信息,编写出代码之后,读者

可以考虑其性能。

2)数据库性能调试的原则

正如前文所叙述的一样,性能主要考虑以下的这些方面,这也可以作为读者考虑某一

个数据库应用的性能时的原则:

I/O操作是应用的 瓶 颈,保 证 事 务 操 作 中 的 每 一 步 都 有 在 合 理 范 围 内 的I/O次

数。通过各种性能监测工具观察,一旦发现I/O次数较大的操作,即应设法改进。

CPU时间也是事务的重要性能指标,可能通过性能预测工具列出事务执行计划,猜测某个事务的瓶颈步骤并设法对其进行优化。

内存访问次数应当限制在合理的范围内,一旦在执行统计信息中发现内存访问次

数(一般体现为内存页面的逻辑读次数)过多,即可以考虑在相关数据列上添加索

引来进行优化。

充分利用事务调度的并行性提高性能的同时,注意监测是否有事务死锁的情况出

现。一旦出现,必将牺牲一些事务来调配,所以死锁的发现越及时越好。

Page 187: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

180  数据库系统实验指导教程

因此,读者在今后的实际应用中,可以采用先依据功能目标写出SQL代码,然后根据

上面的性能调试原则,并采用本章中介绍的性能调试工具,对写出的SQL代码进行调试,

然后写出优化后的代码。

下面,便遵循这一过程进行优化。

3)性能改进后的方案

为了对整个事务进行性能的调试,需要分析事务中每一个步骤的性能情况。注意到

前面介绍的许多工具都是针对一个事务展示一个统计信息,所以为了更好地观察,可以把

上面写出的SQL代码中的核心事务拆分成多个事务。这利用TSQL的“GO”语句就可

实现。

步骤如下:

① 打开查询分析器输入:

犝犛犈犛犮犺狅狅犾

犌犗

犛犈犔犈犆犜犮狅狌狀狋()犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈狊犮狅狉犲>=90

犌犗

犛犈犔犈犆犜犮狅狌狀狋()犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈狊犮狅狉犲犻狊犖犝犔犔

犌犗

犛犈犔犈犆犜狊.狊犻犱,狊.狊狀犪犿犲

犉犚犗犕犆犎犗犐犆犈犛犪狊犮,犛犜犝犇犈犖犜犛犪狊狊

犠犎犈犚犈犮.狊犮狅狉犲>=98犪狀犱犮.狊犻犱=狊.狊犻犱

犌犗

② 接着,先来观测一下 各 个 事 务 的 执 行 计 划。选 中 查 询 分 析 器 中“查 询”菜 单 中 的

“显示估计的执行计划”。

③ 然后运行事务,可以看到图形化的执行计划,如图6.24所示。

图6.24 事件步骤所消耗的比例

Page 188: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

181  第6章 性能监测

第一个选用数据库的事务这里不予考虑,注意到第二个和第三个事务都有一个瓶颈

步骤,占据了整个事务97%和98%的成本,而最后一个事务是整个批处理中的关键部分,占据了查询成本的49.92%。通过这些比例,可以对整个事务的步骤性能有个初步的认

识。而性能调整的关键正是各处瓶颈。针对第二、三个事务的瓶颈,在于“ClusteredIndexScan”的这个步骤上,也就是“扫描

聚簇索引”的这一步上。这很可能是索引建立不当引起的,这必然造成无谓的内存访问过

多。读者可以用鼠标定义前两个事务(也就是“USE”事务和第一个“SFW”事务,这样可

以只运行被定义的事务),并在“当前连接属性”中选择“设 置statisticsIO”选 项,然 后 运

行。显示的结果证实了前面的分析,逻辑读的次数很高,如图6.25所示。这正是索引设

置不当引起的,所以,这里可以通过设置恰当的索引来解决问题。

图6.25 逻辑读取次数过多的事务

可以清楚地看到,几个SFW 事务都是在CHOICES表格的score数据列上进行逻辑

操作的,而数据 表 格score上 却 没 有 建 立 任 何 的 索 引,索 引 改 进 就 是 在 其 上 建 立 一 个

索引:

犆犚犈犃犜犈犐犖犇犈犡狊犮狅狉犲狊犗犖犆犎犗犐犆犈犛(狊犮狅狉犲)

这样便可以利用一次索引操作而大大减少今后在此数据列上的存储器访问次数。这

个索引的建立,同样可以大大减少之后两个SFW事务的逻辑读次数。

④ 接下来,考察占据了将近一半批处理成本的最后一个SFW 事务。这是一个表连

接查询,从数据库理论的知识知道,这种类型的查询必然是资源消耗巨大的。而替代它的

方式就是采用嵌套查询。根据语义,这里把最后一个事务转换成为:

犛犈犔犈犆犜狊犻犱,狊狀犪犿犲犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈狊犻犱犐犖(犛犈犔犈犆犜狊犻犱犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈狊犮狅狉犲>=98)

调整之后的SQL代码成为:

犝犛犈犛犮犺狅狅犾犌犗犆犚犈犃犜犈犐犖犇犈犡狊犮狅狉犲狊犗犖犆犎犗犐犆犈犛(狊犮狅狉犲)

犌犗犛犈犔犈犆犜犮狅狌狀狋()犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈狊犮狅狉犲>=90犌犗

Page 189: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

182  数据库系统实验指导教程

犛犈犔犈犆犜犮狅狌狀狋()犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈狊犮狅狉犲犻狊犖犝犔犔犌犗犛犈犔犈犆犜狊犻犱,狊狀犪犿犲犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈狊犻犱犐犖(犛犈犔犈犆犜狊犻犱犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈狊犮狅狉犲>=98)

犌犗犇犚犗犘犐犖犇犈犡犆犎犗犐犆犈犛.狊犮狅狉犲狊犌犗

最后将索引删除与逻辑无关,仅仅是为了反复调试的方便。

⑤ 此时,读者可以 尝 试 选 中“当 前 连 接 属 性”中 的“设 置statisticsIO”,然 后 执 行 此

SQL批处理,结果说明性能调整是卓有成效的,如图6.26所示。

图6.26 成功优化了逻辑读取次数

可以看到,在建立了索引之后,之后的事务付出的逻辑读次数大大减少了。这正是性

能调试的作用。关于索引的更多细节,可以参见本书的其他章节。

5. 习题

(1)在本章中,介绍了许多功能丰富的性能监测工具,其中有不少功能类似的工具,

例如:SHOWPLAN_TEXT与SHOWPLAN_ALL都可以显示执行 计 划,而“图 形 化 显

示计划”工具也可以获得相似的信息;获取统计信息的多个工具都可以获得运行时间信

息、存储器访问信息等;而事件探查器更是对性能信息的综合跟踪。读者在现实应用中

并不是需要使用每一个工具,也不是简单地使用功能最为全面的几个,而是应该对这些工

具进行综合的评价,选择合适应用需要的性能工具。请仔细分析上面列出的各种类别的

性能工具,考虑适合使用它们的场合。

(2)假设现在需要在本章依据的School数据库上通过SQL语句给出教授课程编号

为10001的教师的详细信息。请读者根据本章“综合应用”的思路来完成这一任务,即首

先写出直观 的 初 步 方 案,然 后 按 照 数 据 库 性 能 调 试 的 原 则、应 用 本 章 所 学 习 的SQL

Server的性能监测工具对初步方案进行调优,然后写出最后的改进方案。希望读者从中

体会对一个事务进行性能调试的方法。

Page 190: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

183  第6章 性能监测

习题答案

实验 6.1

  (1)SETNOEXECON和SETNOEXECOFF。(2)采用的方法和本实验介绍的方法类似。注意多步骤的分析中,会显示各个步骤

在整个事务所占的资源比例,这是一个有用的信息。

实验 6.2

(1)开启统计信息,运行之前的例子,就可以比较得出结论。(2)对于不同的情形,体会客户端与数据库服务端的含义。(3)可以自行尝试,也可以查阅SQLServer2000的连接帮助文件。这里也要借机提

出,利用优秀的开发平台的连接参考,是一个高素质的开发人员应该具备的素质。

实验 6.3

采用和本实验类似的方法,注意设置上限后需要重启数据库服务器。

实验 6.4

(1)根据应用需要设计。后面章节的锁机制的监测也是一个例子,系统自带的模板

也是很好的参考。(2)Oracle等著名商业数据库都有类似机制。

实验 6.5

对计数器的介绍已经在实验中列出。波动一般是出现了事务吞吐量的变化。

实验 6.6

锁机制的确是现代关系数据库系统的核心。结合数据库的原理解析,以及这些工具,读者应该建立起对锁机制更进一步的了解。

实验 6.7

(1)使用这些工具的原则就是,尽量使用可以达到目的的但是尽量简单的工具。因

为,往往存在着一些重型的工具可以完成几乎所有的监测操作,但是往往这种工具会显得

非常臃肿,对于轻量级的应用往往会有资源耗费过大等问题。(2)参照本实验综合应用的思路,特别是数据库性能调试的原则完成。

Page 191: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

C H A P T E R 7

第7章 物 理 优 化

对于一个大型的数据库管理系统,性能调优是每一个数据库管理员的日

常管理中最棘手的问题。由于涉及数据库系统的诸多因素,因此性能调优不

仅需要数据库管理员掌握基本的原理知识,而且还需要对具体的数据库商业

产品、运行的操作系统、应用程序和硬件的物理性能有广泛而深刻的理解。

性能的增长与投入的计算资源的量有密切的关系。比如增 加 内 存 使

系统的缓存增加,减少磁盘访问次数,从而提高系统的性能。当然这样 提

高性能的手段,需要更多的成本支出。而本章开展的前提假设是在原有的

计算资源的基础上,对系统进行性能调优,着重讨论如何正确地选择、维护

和使用索引的方法。索引的建立是为了提高系统的访问性能,然而并非任何对数据库的访

问操作都能有 效 地 利 用 索 引,且 并 非 任 意 创 建 的 索 引 都 是 有 用 的。一 方

面,索引需要付出维护的代价,尤其在插入、删除和某些更新时,索引会产

生系统开销,如果建立在某属性上的索引从未使用,那么系统的性能将会

相应下降。另一方面,由于索引并非适合于任何类型的查询,因此在某 个

属性上建立索引时,需要考虑对该属性的查询操作类型是否将会利用此索

引,否则无形之中就会产生像前面所说的没使用索引的资源浪费。以下实验数据均是在作者机器上进行的,配置是512MBDDR,P43.0,

WindowsXPSP2,SQLServer2000。由于系统资源的占用等问题,实验中

连续执行两次同一个查询的时间也是不同的,下面实验数据多是几次实验

的平均值,或者是取一个合理的值。如果读者发现自己的数据和书上数据

不一样,是正常的事,只要合乎理论就行。

实验7.1 建立索引

1. 实验目的

  学会在SQLServer中建立索引。

Page 192: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

185  第7章 物理优化

2. 原理解析

在SQLServer中要建立索引有如下三种方法。(1)可以在查询分析器中执行以下语句:

犆犚犈犃犜犈犐犖犇犈犡[狌狀犻狇狌犲/犮犾狌狊狋犲狉犲犱/…]犻狀犱犲狓_狀犪犿犲犗犖狋犪犫犾犲_狀犪犿犲(犮狅犾狌犿狀_狀犪犿犲)[犪狊犮/犱犲狊犮][狑犻狋犺…]

(2)可以使用企业管理器提供的“创建索引向导”,这个功能在“工具”|“向导”|“数据

库”|“创建索引向导”里能找到。(3)可以使用企业管理器提供的索引管理器,如图7.1所示,打开索引管理器的方法

是:选择一个表,然后在快捷方式菜单或者“操作”菜单上选择“所有任务”|“管理索引”菜

单项。

图7.1 企业管理器的“管理索引”对话框

3. 实验内容

建立一个test表,字段为(ID,NAME,AGE,PHONE),ID为系统自动生成的惟一标

识。并在NAME上建立聚簇索引,PHONE上建立非聚簇索引,并分析所遇到的问题。

4. 实验步骤

(1)打开查询分析器;

Page 193: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

186  数据库系统实验指导教程

(2)先建立TEST表,代码如下(见7_1_1.sql):

犝犛犈犛犮犺狅狅犾犌犗犆犚犈犃犜犈犜犃犅犔犈犜犈犛犜(

 犐犇犝犖犐犙犝犈犐犇犈犖犜犐犉犐犈犚犖犗犜犖犝犔犔   犚犗犠犌犝犐犇犆犗犔犇犈犉犃犝犔犜(犖犈犠犐犇())

   犘犚犐犕犃犚犢犓犈犢,

 犖犃犕犈犞犃犚犆犎犃犚(50)犖犗犜犖犝犔犔,

 犃犌犈犐犖犜,

 犘犎犗犖犈犆犎犃犚(20)犖犗犜犖犝犔犔)犌犗

(3)在PHONE上建立非聚簇索引,代码如下(见7_1_2.sql):

犝犛犈犛犮犺狅狅犾犌犗犆犚犈犃犜犈犐犖犇犈犡犐犡_犘犎犗犖犈犗犖犜犈犛犜(犘犎犗犖犈)犌犗

(4)在AGE上建立聚簇索引,代码如下(见7_1_3.sql):

犝犛犈犛犮犺狅狅犾犌犗犆犚犈犃犜犈犆犔犝犛犜犈犚犈犇犐犖犇犈犡犐犡_犖犃犕犈犗犖犜犈犛犜(犖犃犕犈)犌犗

结果分析:第(4)步显示出错,出错信息为:“不能在表‘TEST’上创建多个聚簇索引。请在创建

新聚簇索引前除去现有的聚簇索引‘PK_TEST_2E1BDC42’。”为什么呢?这里并没有创

建其他聚簇索引。答案是SQLServer在建表时默认为主键建立聚簇索引,本例中,系统

在ID上自动建立了聚簇索引(到企业管理器里看看是否如此)。但是很多时候(应该说是

建立数据表的好的模式)一个表的主键是没有任何意义的,只是一行的唯一标识,这样在

此列上建立的聚簇索引就没有意义了,而一个表又只能建立一个聚簇索引,故常常在建表

时显式取消主键上的聚簇索引(用NONCLUSTERED关键字),或建立表后显式取消主

键上的聚簇索引。故把第(2)步改为(见7_1_4.sql):

犝犛犈犛犮犺狅狅犾犌犗犆犚犈犃犜犈犜犃犅犔犈犜犈犛犜(

 犐犇犝犖犐犙犝犈犐犇犈犖犜犐犉犐犈犚犖犗犜犖犝犔犔   犚犗犠犌犝犐犇犆犗犔犇犈犉犃犝犔犜(犖犈犠犐犇())

   犘犚犐犕犃犚犢犓犈犢犖犗犖犆犔犝犛犜犈犚犈犇,

 犖犃犕犈犞犃犚犆犎犃犚(50)犖犗犜犖犝犔犔,

 犃犌犈犐犖犜,

 犘犎犗犖犈犆犎犃犚(20)犖犗犜犖犝犔犔

Page 194: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

187  第7章 物理优化

犌犗

或者把第(4)步改为(见7_1_5.sql):

犝犛犈犛犮犺狅狅犾犌犗犇犈犆犔犃犚犈@狀犪犿犲犞犃犚犆犎犃犚(20)

犛犈犔犈犆犜@狀犪犿犲=狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊犠犎犈犚犈犻犱=狅犫犼犲犮狋_犻犱('狋犲狊狋')

犈犡犈犆('犪犾狋犲狉狋犪犫犾犲犜犈犛犜犱狉狅狆犮狅狀狊狋狉犪犻狀狋'+@狀犪犿犲)

犌犗犃犔犜犈犚犜犃犅犔犈犜犈犛犜犠犐犜犎犖犗犆犎犈犆犓犃犇犇 犆犗犖犛犜犚犃犐犖犜[犘犓_犐犇]犘犚犐犕犃犚犢犓犈犢犖犗犖犆犔犝犛犜犈犚犈犇([犐犇])

犌犗犆犚犈犃犜犈犆犔犝犛犜犈犚犈犇犐犖犇犈犡犐犡_犖犃犕犈犗犖犜犈犛犜(犖犃犕犈)

犌犗

上述先删掉ID上的主键约束(同时删除了其上的聚簇索引),再在ID上加上主键约

束,但是没有在上面建立聚簇索引。最后在NAME字段上建立了聚簇索引。

5. 习题

自己动手试一试在STUDENTS表上为sname字段建立聚簇索引,为email字段建

立非聚簇索引。

实验7.2 覆盖索引的作用

1. 实验目的

  通过本实验体会覆盖索引的作用,在以后的实践中,能适时地使用覆盖索引来提高数

据库的性能。

2. 原理解析

覆盖索引是指那些索引项中包含查询所需要的全部信息的非聚簇索引,可以是单索

引或复合索引,但是一般都是非聚簇的。这种索引之所以比较快也正是因为索引页中包

含了查询所必需的数据,不需再去访问数据页。什么时候使用覆盖索引呢?经常同时存取多列,且每列都含有重复值则可考虑建立

复合索引来覆盖一个或一组查询,并把查询引用最频繁的列作为前导列,如果可能尽量使

关键查询形成覆盖查询。

3. 实验内容

数据库School的选课表CHOICES有如下结构:

犆犎犗犐犆犈犛(狀狅,狊犻犱(学生犐犇),狋犻犱(教师犐犇),犮犻犱(课程犐犇),狊犮狅狉犲)

Page 195: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

188  数据库系统实验指导教程

假设选课表集中用于查询分析,经常执行统计某课程修读的学生人数查询访问。对

于这个查询,很明显只要通过对课程ID建立非聚簇索引,那么该索引便可以覆盖此类查

询。这样,此类查询只需扫描索引页,而不必访问数据页。

4. 实验步骤

1)要求

(1)首先执行没有索引的实验(设数据库CHOICES表在cid列上没有索引);(2)然后做有索引的实验;(3)对比实验结果。

2)分析与解答

(1)先输入以下语句执行(见7_2_1.sql):

犝犛犈犛犮犺狅狅犾犐犉犈犡犐犛犜犛(犛犈犔犈犆犜狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊

  犠犎犈犚犈狀犪犿犲='犮狊_犮犻犱_犻狀犱')

  犇犚犗犘犐犖犇犈犡犆犎犗犐犆犈犛.犮狊_犮犻犱_犻狀犱犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犛犈犔犈犆犜犆犗犝犖犜()

犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犮犻犱=10040犌犗

系统输出结果是:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读2476次,物理读0次,预读38次。

犛犙犔犛犲狉狏犲狉执行时间:

  犆犘犝时间=94毫秒,耗费时间=945毫秒。

(2)然后输入以下语句并执行(见7_2_2.sql):

犝犛犈犛犮犺狅狅犾犐犉犖犗犜犈犡犐犛犜犛(犛犈犔犈犆犜狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊

   犠犎犈犚犈狀犪犿犲='犮狊_犮犻犱_犻狀犱')

  犆犚犈犃犜犈犐犖犇犈犡犮狊_犮犻犱_犻狀犱

  犗犖犆犎犗犐犆犈犛(犆犐犇)

犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犛犈犔犈犆犜犆犗犝犖犜()

Page 196: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

189  第7章 物理优化

犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犮犻犱=10040犌犗

系统输出结果是:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读632次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=89毫秒,耗费时间=89毫秒。

结果分析:对比上面没有建立覆盖索引的耗费时间,效率大大提高,本实验中没有覆盖索引的查

询比有覆盖索引的查询慢了10倍有余。

5. 习题

假设经常会有这类查询:查询某学生选的课的课程号。该如何建立索引,自己试试

看建索引前后的查询效率的差别。

实验7.3 聚簇索引

1. 实验目的

  通过实验体会聚簇索引的优缺点,学会根据具体情况创建聚簇索引。

2. 原理解析

1)聚簇索引优点

如果聚簇索引是稀疏的,则它比稠密索引存储的指针少(非聚簇索引不能是稀疏的)。正如前面讨论过的,如果记录较小,则在记录访问中有可能可以减少磁盘存取的次数;聚

簇索引有利于多点查询,因为值相同的记录放在了一起(一个页内),这样一次磁盘访问就

可以了,如果是非聚簇索引,因为可能存在不同的页上,可能需要好几次磁盘访问。同样

的原因,聚簇索引有助于在不同值较少的属性上进行等值连接;基于B树结构的聚簇索

引,可以很好地支持范围查询、前缀匹配查询和排序查询,节省存储空间。聚簇以后,聚簇

码相同的元组集中在一起了,因而聚簇码值不必在每个元组中重复存储,只要在一组中存

一次就行了。

2)聚簇索引缺点

如果存在大量的溢出数据页,它的性能会下降。原因是,访问这些页面的磁盘定位需

要花费很多时间。还有就是建立与维护聚簇的开销相当大。

3. 实验内容

还是以School数据库中CHOICES表举例,设建表时考虑到以后经常有一个用sid查询此学生所有选课信息的查询,考虑到一般学生不止选一门课,且要询问这些记录的所

Page 197: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

190  数据库系统实验指导教程

有信息,故在sid上建立聚簇索引,使相同sid的记录存在一起,取数据页面时能一起取出

来,减少数据页面的存取次数。

4. 实验步骤

(1)在没有任何索引的情况,执行以下语句(见7_3_1.sql):

犝犛犈犛犮犺狅狅犾犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗狊犲犾犲犮狋犳狉狅犿犮犺狅犻犮犲狊狑犺犲狉犲狊犻犱犾犻犽犲'800554358'

犌犗

执行后得到的结果如下:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读2476次,物理读0次,预读28次。

犛犙犔犛犲狉狏犲狉执行时间:

  犆犘犝时间=313毫秒,耗费时间=646毫秒。

(2)在sid上建有非聚簇索引的情况,执行以下语句(见7_3_2.sql):

犝犛犈犛犮犺狅狅犾犐犉犖犗犜犈犡犐犛犜犛(犛犈犔犈犆犜狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊

 犠犎犈犚犈狀犪犿犲='犐犡_狊犻犱')

犆犚犈犃犜犈犐犖犇犈犡犐犡_狊犻犱犗犖犆犎犗犐犆犈犛(犛犐犇)

犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗狊犲犾犲犮狋犳狉狅犿犮犺狅犻犮犲狊狑犺犲狉犲狊犻犱犾犻犽犲'800554358'

犌犗

执行后得到的运行时间结果如下:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读15次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

  犆犘犝时间 =0毫秒,耗费时间 =51毫秒。

(3)在sid上建有聚簇索引的情况,执行以下语句(见7_3_3.sql):

犝犛犈犛犮犺狅狅犾犐犉犈犡犐犛犜犛(犛犈犔犈犆犜狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊

  犠犎犈犚犈狀犪犿犲='犐犡_狊犻犱')

  犇犚犗犘犐犖犇犈犡犆犎犗犐犆犈犛.犐犡_狊犻犱犌犗

Page 198: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

191  第7章 物理优化

犆犚犈犃犜犈犆犔犝犛犜犈犚犈犇犐犖犇犈犡犐犡_狊犻犱犗犖犆犎犗犐犆犈犛(犛犐犇)犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗狊犲犾犲犮狋犳狉狅犿犮犺狅犻犮犲狊狑犺犲狉犲狊犻犱犾犻犽犲'800554358'

犌犗

执行后得到的运行结果是:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读3次,物理读0次,预读0次。犛犙犔犛犲狉狏犲狉执行时间:

  犆犘犝时间 =0毫秒,耗费时间 =14毫秒。

结果分析:比较一下,没有索引时最慢,有非聚簇索引时比较快,建有聚簇索引时最快。但是建

立聚簇索引时需要很长时间,因为要重组记录,且以后更新时(如插入记录)也需移动很多

条记录,维护所需代价很大。而且聚簇索引每个表只能建一个,所以建立聚簇索引时需要

全面考虑。

5. 习题

分别试试在三种情况下(无索引、有非聚簇索引、有聚簇索引)向一个表中插入记录的

耗时状况,看看是否与分析的结论一致。

实验7.4 维护索引的代价

1. 实验目的

  索引对于查询等操作是很有帮助的,但是建立和维护索引也是需要额外开销的,空间

上和时间上都需要。本实验帮助读者理解聚簇索引的开销,由此可以看到,索引并不都是

有用的,索引不是建得越多越好,不必要的索引会给系统带来负担。

2. 原理解析

建立索引时需要不少时间和空间,删除和更新索引也需一定时间。特别指出:如果

是建立、删除、更新聚簇索引,则需要更多时间,因为可能需要重新排列所有记录的顺序。所以,维护聚簇索引的代价是比较高的,如果一个聚簇索引基本上用不到,则这些代价就

是多余的,该聚簇索引不但不会提高数据库操作的速度,反而会使速度变慢,故建立聚簇索

引一定要先想想到底这个索引能不能起到很大作用。下面会通过几个实验来分别说明。

3. 实验内容

CHOICES表结构如下:

Page 199: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

192  数据库系统实验指导教程

犆犎犗犐犆犈犛(狀狅(犻狀狋),狊犻犱(犮犺犪狉(10)),狋犻犱(犮犺犪狉(10)),犮犻犱(犮犺犪狉(10)),狊犮狅狉犲(犻狀狋))

分别看看此表在没有索引的情况下更新操作的用时、建立聚簇索引的用时、有聚簇索

引时更新操作的用时和删除聚簇索引的用时。

4. 实验步骤

(1)首先打开SQL查询分析器;(2)把原先有的索引删除,方便实验。执行以下语句(见7_4_1.sql):

犝犛犈犛犮犺狅狅犾犌犗犐犉犈犡犐犛犜犛(犛犈犔犈犆犜狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊犠犎犈犚犈狀犪犿犲= '犐犡_狊犻犱')

  犇犚犗犘犐犖犇犈犡犆犎犗犐犆犈犛.犐犡_狊犻犱犌犗犐犉犈犡犐犛犜犛(犛犈犔犈犆犜狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊犠犎犈犚犈狀犪犿犲='犐犡_狋犻犱')

  犇犚犗犘犐犖犇犈犡犆犎犗犐犆犈犛.犐犡_狋犻犱犌犗

(3)更新表的记录并查看用时,执行以下语句(见7_4_2.sql):

犝犛犈犛犮犺狅狅犾犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犝犘犇犃犜犈犆犎犗犐犆犈犛狊犲狋狊犮狅狉犲=狊犮狅狉犲-1犌犗

运行完成得到运行时间:

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=1015毫秒,耗费时间=1902毫秒。

(4)创建一个聚簇索引查看用时,执行以下语句(见7_4_3.sql):

犝犛犈犛犮犺狅狅犾犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犆犚犈犃犜犈犮犾狌狊狋犲狉犲犱犐犖犇犈犡犐犡_狊犻犱犗犖犆犎犗犐犆犈犛(犛犐犇)

犌犗

运行完成得到运行时间:

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=6719毫秒,耗费时间=7155毫秒。

(5)在有一个聚簇索引时,执行更新操作,并查看用时(用表扫描来更新),执行以下

语句(见7_4_4.sql):

Page 200: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

193  第7章 物理优化

犝犛犈犛犮犺狅狅犾犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犝犘犇犃犜犈犆犎犗犐犆犈犛狊犲狋狊犮狅狉犲=狊犮狅狉犲+1犌犗

运行完成得到运行时间:

犛犙犔犛犲狉狏犲狉执行时间: 犆犘犝时间=7109毫秒,耗费时间=10876毫秒。

(6)最后删除该聚簇索引并查看用时,执行以下语句(见7_4_5.sql):

犝犛犈犛犮犺狅狅犾犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犐犉犈犡犐犛犜犛(犛犈犔犈犆犜狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊犠犎犈犚犈狀犪犿犲= '犐犡_狊犻犱')犇犚犗犘犐犖犇犈犡犆犎犗犐犆犈犛.犐犡_狊犻犱犌犗

运行完成得到运行时间:

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=2578毫秒,耗费时间=2610毫秒。

结果分析:结果和预计的符合,在此实验中,因为更新时是数据库中所有的数据都更新,故此时

索引是没有任何帮助的,但更新索引需要时间,可以看到有聚簇索引时的更新数据时间比

没有聚簇索引时的时间多得多。另外,创建和删除聚簇索引也需用时,特别创建时的用时

比较多。

5. 习题

试试在CHOICES表上建立几个非聚簇索引,看看建立和删除非聚簇索引所需时间。再比较有非聚簇索引时和没有时更新的速度。

实验7.5 冗余索引

1. 实验目的

  如实验7.4中所说,索引虽然是一种很有用的查询结构,但会给系统带来额外的负

担,而且有的情况下有的索引是冗余的,对提高查询速度也没有帮助。本实验通过一个例

子说明了这个问题,并非任意创建索引都是有用的。

2. 原理解析

建立索引的最终目的是尽可能地减少数据库对磁盘的访问次数。如果一个索引不能

Page 201: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

194  数据库系统实验指导教程

明显降低其目的操作的磁盘访问次数,这个索引就是失败的。比如有一个表为Student(name#,age,gender),一个元组占100个字节,假设当前

数据库的一个物理数据页面为8KB,故一页大约存有80个元组,这个表已经在name字

段上建立了聚簇索引,而现在问能否在age或gender字段上建立B树结构的非聚簇索引

以提高对age和gender的查询速度(如这些查询:selectfromStudentwhereage=24,或selectfromStudentwheregenderlike'male')。

答案是不会有明显提高。分析一下:对age来说,一个Student的age的范围充其量

不过几岁到几十岁,这样就算age的范围有80这么大(就是age字段有80个不同的值),又因为age上建立的是非聚簇索引,设age的不同值是在数据库中均匀分散的。而由上

面知每页有80个元组,那么无论查询的age是什么值,每页平均都会有一个元组符合要

求,可以知道数据库对磁盘的操作的最小单位是一个数据页,要么不读磁盘,要么至少一

个磁盘页面整个读入内存,这样的话,像selectfromStudentwhereage=24这类查

询还是会几乎每个页面都要访问,和没有索引基本上没有区别,都是整个表扫描(其实如

果直接全表扫描的话,反而会更快,具体原因请参考相关书籍)。而索引的建立和维护却

还需要额外的开支,故这个索引是没有意义的。同理,gender的范围更窄,只有两个值(male,female),不管哪个值平均每页都会有许

多,在上面建立B树结构的索引是没有任何意义的(不过对这种值范围非常小的字段可

以建立位图结构的索引,具体原理请参考相关书籍)。

3. 实验内容

数据库School的表STUDENTS有如下结构:

犛犜犝犇犈犖犜犛(狊犻犱(犮犺犪狉(10)),狊狀犪犿犲(犮犺犪狉(30)),犲犿犪犻犾(犮犺犪狉(30)),犵狉犪犱犲(犻狀狋))

若经常需要对表STUDENTS的属性grade进行多值查询,那么是否有必要在对属

性grade进行非聚簇索引?(假设数据库建立于SQLServer2000,且表STUDENTS的

属性grade是一个均匀分布。)从表STUDENTS的结构可计算出,每一个元组记录的大小为10+30+30+4=74B

(其中char型为1B,int型为4B)。SQLServer2000中,每个数据页的大小为8KB,那么

每个数据页中约含有80个 元 组 记 录。经 测 算,表STUDENTS的grade属 性 约 含 有15个不同值,也就是说给定的某具体grade的多值查询,每页中大约有80/15=6个该grade的元组。很明显,在这种情况下,由于元组记录散落于各个数据页,基本上是每个数据页

都需要访问,故即使在没索引下的全表扫描执行的速度也不比有索引下的查询慢。

4. 实验步骤

(1)首先打开SQL查询分析器。(2)执行如下语句(见7_5_1.sql):

犝犛犈犛犮犺狅狅犾犐犉犈犡犐犛犜犛(犛犈犔犈犆犜狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊犠犎犈犚犈狀犪犿犲='狊狋_犻狀犱')

Page 202: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

195  第7章 物理优化

犇犚犗犘犐犖犇犈犡犛犜犝犇犈犖犜犛.狊狋_犻狀犱犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犛犈犔犈犆犜犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈犵狉犪犱犲=2000犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犉犉犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犉犉犌犗

(3)再执行对grade建立非聚簇索引的查询脚本(见7_5_2.sql):

犝犛犈犛犮犺狅狅犾犐犉犖犗犜犈犡犐犛犜犛(犛犈犔犈犆犜狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊犠犎犈犚犈狀犪犿犲='狊狋_犻狀犱')

犆犚犈犃犜犈犐犖犇犈犡狊狋_犻狀犱犗犖犛犜犝犇犈犖犜犛(犵狉犪犱犲)

犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犛犈犔犈犆犜犉犚犗犕犛犜犝犇犈犖犜犛犠犎犈犚犈犵狉犪犱犲=2000犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犉犉犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犉犉犌犗

结果分析:从SQLServer返回结果看出,两者执行速度差异不大,建立索引后执行速度只稍微

快4%。如果再对比添加索引后,对增、删、改操作所带来的性能损失,就已经不值得利用

这一索引了。

5. 习题

在CHOICES表里建立SCORE和cid字段上的非聚簇索引,并执行查询试一试看对

查询有没有性能的提升。

实验7.6 表中多个索引的选择

1. 实验目的

  通过实验了解单表多索引在执行查询时对索引的选择规律。便于以后创建更加有用

的索引。

Page 203: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

196  数据库系统实验指导教程

2. 原理解析

如果一个表中建有多个索引,在执行查询时系统将利用全部能用的索引还是只选择

其中一部分使用?如果只选择一部分,那么系统将如何选择呢?

比如:索引有C1Xon(C1)、C2Xon(C2)、C3Xon(C3)、C4X(C4),查询为:

狊犲犾犲犮狋犳狉狅犿犜狑犺犲狉犲犆1犅犈犜犠犈犈犖20犃犖犇30犃犖犇犆2=5犃犖犇犆3=11犃犖犇犆4="狑犪狀犵"

存取的基本思想是:(1)根据第一个索引取出匹配的所有RID列表,进行排序;(2)根据第二个索引取出匹配的所有RID列表,进行排序;(3)将两个RID列表作逻辑运算,形成RID1;(4)根据第三个索引取出匹配的所有RID列表,进行排序;(5)将RID1与第三个索引形成的RID列表作逻辑运算,形成RID2;(6)根据结果RID集采用列表预取(使用列表预取方式的条件后面给出)取出相关数

据页面。以上算法还有两个问题,第一个是如何确定索引选择的顺序。第二个是这个算法并

不是一直做完所有的索引为止,这里就有一个什么时候停止的问题。第一个问题的解决方法如下:按照匹配扫描的过滤因子递增的顺序放置索引,首先选择带有最小过滤因子的。然

后是过滤因子第二小的,如此下去。第二个问题的解决需要系统计算出选择各个索引时大约需要的时间,然后选择最优

的。具体如何计算各个计划的时间请参考相关书籍。

3. 实验内容

CHOICES表结构如下:

犆犎犗犐犆犈犛(狀狅(犻狀狋),狊犻犱(犮犺犪狉(10)),狋犻犱(犮犺犪狉(10)),犮犻犱(犮犺犪狉(10)),狊犮狅狉犲(犻狀狋))

假设要查 询CHOICES表 里 学 生ID在888000000和888900000之 间,教 师ID在

229850000和229900000之间的所有记 录。从 表CHOICES的 结 构 可 计 算 出,每 一 个 元

组记录的大小为4+10+10+10+4=38B(其中char型为1B,int型为4B),SQLServer2000中,每个数据页的大小为8KB,那么每个数据页中约含有200个元组记录,而据统计

课程号cid只有50个不同值,即平均每数据页各个课程号都有四个,根据实验7.5知,在

cid上即使有索引也是没有用处的。同理,分数最多也只有100个不同值,score上即使有

索引也用不到。故在表上建立两个索引:sid上建立IX_sid非聚簇索引,tid上建立IX_tid非聚簇索引。然后分别执行:

第一个 查 询,学 生ID 在888000000和888900000之 间,教 师ID 在229850000和

229900000之间的所有记录;

Page 204: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

197  第7章 物理优化

第二个 查 询,学 生ID 在888000000和888900000之 间,教 师ID 在229000000和

229900000之间的所有记录;第三个 查 询,学 生ID 在850000000和888900000之 间,教 师ID 在229500000和

229900000之间的所有记录。看看SQLServer2000的索引所用情况。

4. 实验步骤

(1)首先打开SQL查询分析器。(2)建立索引,执行如下语句(见7_6_1.sql):

犝犛犈犛犮犺狅狅犾犐犉犖犗犜犈犡犐犛犜犛(犛犈犔犈犆犜狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊犠犎犈犚犈狀犪犿犲='犻狓_狊犻犱')

犆犚犈犃犜犈犐犖犇犈犡犻狓_狊犻犱犗犖犆犎犗犐犆犈犛(狊犻犱)

犌犗犐犉犖犗犜犈犡犐犛犜犛(犛犈犔犈犆犜狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊犠犎犈犚犈狀犪犿犲='犻狓_狋犻犱')

犆犚犈犃犜犈犐犖犇犈犡犻狓_狋犻犱犗犖犆犎犗犐犆犈犛(狋犻犱)

犌犗

(3)然后执行第一个查询(见7_6_2.sql):

犛犈犜犛犎犗犠犘犔犃犖_犃犔犔犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗狊犲犾犲犮狋犳狉狅犿犆犎犗犐犆犈犛狑犺犲狉犲狊犻犱犫犲狋狑犲犲狀'888000000'犪狀犱 '888900000'犪狀犱狋犻犱犫犲狋狑犲犲狀 '229850000'

犪狀犱'229900000'

犌犗

在Stmttext栏中看到:

|犐狀犱犲狓犛犲犲犽(犗犅犑犈犆犜:([犛犮犺狅狅犾].[犱犫狅].[犆犎犗犐犆犈犛].[犐犡_狋犻犱])…

|犐狀犱犲狓犛犲犲犽(犗犅犑犈犆犜:([犛犮犺狅狅犾].[犱犫狅].[犆犎犗犐犆犈犛].[犐犡_狊犻犱])…

说明系统使用了IX_tid索引和IX_sid索引,且系统首先选择使用了IX_tid。(4)然后执行第二个查询(见7_6_3.sql):

犛犈犜犛犎犗犠犘犔犃犖_犃犔犔犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗狊犲犾犲犮狋犳狉狅犿犆犎犗犐犆犈犛狑犺犲狉犲狊犻犱犫犲狋狑犲犲狀'888000000'犪狀犱 '888900000'犪狀犱狋犻犱犫犲狋狑犲犲狀 '229000000'

犪狀犱'229900000'

Page 205: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

198  数据库系统实验指导教程

犌犗

在Stmttext栏中看到:

|犐狀犱犲狓犛犲犲犽(犗犅犑犈犆犜:([犛犮犺狅狅犾].[犱犫狅].[犆犎犗犐犆犈犛].[犐犡_狊犻犱])

|犐狀犱犲狓犛犲犲犽(犗犅犑犈犆犜:([犛犮犺狅狅犾].[犱犫狅].[犆犎犗犐犆犈犛].[犐犡_狋犻犱])

说明系统使用了IX_tid索引和IX_sid索引,且系统首先选择使用了IX_sid。(5)最后执行第三个查询(见7_6_4.sql):

犛犈犜犛犎犗犠犘犔犃犖_犃犔犔犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗狊犲犾犲犮狋犳狉狅犿犆犎犗犐犆犈犛狑犺犲狉犲狊犻犱犫犲狋狑犲犲狀'850000000'犪狀犱 '889000000'犪狀犱狋犻犱犫犲狋狑犲犲狀 '229500000'

犪狀犱'229900000'

犌犗

在Stmttext栏中看到:

|犉犻犾狋犲狉(犠犎犈犚犈([犆犎犗犐犆犈犛].[狊犻犱]>='850000000'犃犖犇[犆犎犗犐犆犈犛].[狊犻犱]<='889000000'))

       |犐狀犱犲狓犛犲犲犽(犗犅犑犈犆犜:([犛犮犺狅狅犾].[犱犫狅].[犆犎犗犐犆犈犛].[犐犡_狋犻犱])

说明系统只使用了IX_tid索引而没有使用IX_sid索引,而把sid的条件作为了后面

的过滤条件。结果分析:对于第一个查询,在结果中可以看到对于IX_tid的indexseek的EstimateRows为

9.9950495,而对于IX_sid的indexseek的EstimateRows为1053.1788,显 然 索 引IX_tid的过滤因子大,故系统先选择了使用索引IX_tid。

对于第二个查询,在结果中可以看到对于IX_tid的indexseek的EstimateRows为

2258.8362,而对于IX_sid的indexseek的EstimateRows为1053.1788,显然索引IX_sid的过滤因子大,故系统先选择了使用索引IX_sid。

而对于第三个查询,在 结 果 中 可 以 看 到 对 于IX_tid的indexseek的EstimateRows为489.84503,而 粗 略 计 算 一 下 在 此 查 询 中IX_sid索 引 的 过 滤 因 子:(889000000-850000000)/(900000000-800000000)=0.39,而由前面知每数据页大约有200条记录,即每数据页满足在(850000000,889000000)范围内的记录大约有200×0.39=78条,故在

此,使用IX_sid索引是没有用处的,还是每个数据页都要访问,故系统没有采用IX_sid索

引,而把这个条件作为使用了IX_tid索引后的过滤条件。

5. 习题

假设sid和tid上都有非聚簇索引,执行如下查询(见7_6_5.sql):

犛犈犜犛犎犗犠犘犔犃犖_犃犔犔犗犖

Page 206: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

199  第7章 物理优化

犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗狊犲犾犲犮狋犳狉狅犿犆犎犗犐犆犈犛狑犺犲狉犲狊犻犱犫犲狋狑犲犲狀 '880000000'犪狀犱 '889000000'犪狀犱狋犻犱犫犲狋狑犲犲狀 '229850000'

犪狀犱'229900000'

犌犗

看看查询计划,用了哪些索引,为什么?

实验7.7 索引与表的连接查询

1. 实验目的

  表的连接查询是很多数据库操作中涉及的一大类查询,且连接查询也是比较费时的

一类查询。本实验通过几个例子让读者简单了解如何提高连接查询的效率。

2. 原理解析

表R和表S的连接操作有几种,一种最简单的方法是对表R中的每一行,扫描表S的所有行来进行匹配。这种方法称为朴素的嵌套循环连接(naivenestedloopjoin)。如

果S表上没有索引,那么花费的时间与表R和表S大小的乘积(|R|×|S|)成正比。显

然,这是非常低效的,因为即使是中等规模的表(数百万记录)间的连接,也要进行数万亿

次比较。但如果S表上有索引,那么连接可以这样进行:对于表R的每一行记录,通过索

引找到S表上相应的记录来连接,花费的时间大概正比于(|R|×log(|S|))。因此这种

索引嵌套循环连接(indexednestedloopjoin)比起朴素的嵌套循环连接要高效得多。且

如果查询在索引覆盖连接条件的情况下,索引的效用更加明显。另外还有一种等值连接的方法:如果R和S都没有索引时,首先分别在连接属性上

使用同一个哈希(Hash)函数将两个表划分为多个桶,然后再将相对应的桶进行连接。如

果可以近似认为连接的属性在一个表中没有相同值(是一个表的码),那么这种方法花费

的时间正比于R和S大小之和(|R|+|S|),这是比较好的结果。可以看出,在一些情况

下,哈希连接比索引嵌套循环还要快。在没有索引可以用时,SQLServer默认处理就是

采用的哈希连接方法。实现等值连接(R.A=S.B)的通常做法有如下几种:(1)如果在表S中属性B上不同值的个数几乎和总元组数一样多(这一条是很有用

的,因为大多数连接都是外码连接),且R的记录数小于 |S|log(|S|)-1

时(直观上理解,就

是如果R上的记录数比S上的记录数少得多。可以看出,如果R的记录数比S的记录数

多时,就不满足该条件),使用S.B上的索引进行嵌套循环连接比哈希连接效果要好(如

果R中的A属性类似于码,也就是属性的取值几乎都是唯一的,那么使用索引进行嵌套

Page 207: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

200  数据库系统实验指导教程

循环连接也同样比哈希连接效果要好)。(2)如果索引覆盖了连接条件,那么无论S.B中不同值的数目有多少,索引嵌套循环

连接的效果都会比哈希索引要好。因为实际上访问S表的数据只需访问其索引。(3)如果表S以B属性聚簇存储,那么无论S.B中不同值的数目有多少,索引嵌套循

环连接的效果 都 会 比 哈 希 索 引 好。因 为S表 上 具 有 相 同 值 的 元 组 将 会 被 聚 簇 存 放 在

一起。(4)其他情况下,哈希索引的效果更好。另外,在非等值连接(R.A>S.B)中,在连接属性上建立的B树索引(聚簇更好),可

以使用嵌套循环方法避免全表扫描。如果R.A和S.B上的索引都是有序的,如B树(聚

簇更好),则使用归并连接(mergescanjoin)可以进行得更快。方法和两个有序队列的合

并相似(设顺序是从小到大),开始两个队列头上都有一个指针,比较指针所指的值的大

小,如果一边较大,则把另一表的指针移动一格,这样,两个表都只需扫描一次。

3. 实验内容

执行 查 询,查 看STUDENTS表 中grade为2000的 所 有 学 生 的 所 有 成 绩(存 在

CHOICES表的score中)。在STUDENTS表和CHOICES表的sid字段都没有索引的情况和都有聚簇索引的

情况下,比较SQLServer的执行计划和效率。

4. 实验步骤

(1)在企业管理器中分别把两个表中sid上的索引删除掉。(2)打开SQL查询分析器。(3)查看没有索引条件下的此连接的执行计划,执行如下语句(见7_7_1.sql):

犝犛犈犛犮犺狅狅犾犌犗犛犈犜犛犎犗犠犘犔犃犖_犃犔犔犗犖犌犗狊犲犾犲犮狋狊犮狅狉犲犳狉狅犿犛犜犝犇犈犖犜犛,犆犎犗犐犆犈犛狑犺犲狉犲犛犜犝犇犈犖犜犛.犵狉犪犱犲 = 2000犪狀犱犛犜犝犇犈犖犜犛.狊犻犱 =犆犎犗犐犆犈犛.狊犻犱犌犗

执行完毕得到执行计划:

|犎犪狊犺犕犪狋犮犺(犐狀狀犲狉犑狅犻狀…)

  |犜犪犫犾犲犛犮犪狀(…)

  |犜犪犫犾犲犛犮犪狀(…)

(4)执行没有索引条件下的连接,查看用时,语句如下(见7_7_2.sql):

犝犛犈犛犮犺狅狅犾犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖

Page 208: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

201  第7章 物理优化

犌犗犛犈犜犛犎犗犠犘犔犃犖_犃犔犔犗犉犉犌犗狊犲犾犲犮狋狊犮狅狉犲犳狉狅犿犛犜犝犇犈犖犜犛,犆犎犗犐犆犈犛狑犺犲狉犲犛犜犝犇犈犖犜犛.犵狉犪犱犲 = 2000犪狀犱犛犜犝犇犈犖犜犛.狊犻犱 =犆犎犗犐犆犈犛.狊犻犱犌犗

执行得到结果:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读1943次,物理读0次,预读0次。

表 '犛犜犝犇犈犖犜犛'。扫描计数1,逻辑读1031次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间 =297毫秒,耗费时间 =334毫秒。

(5)在企业管理器中把STUDENTS表的sid字段设为主键(系统自动为主键建立聚

簇索引),在CHOICES表中为sid建立聚簇索引(把主键no上的聚簇索引去掉)。(6)在有聚簇索引条件下查看该查询的执行计划,执行如下语句(见7_7_3.sql):

犝犛犈犛犮犺狅狅犾犌犗犛犈犜犛犎犗犠犘犔犃犖_犃犔犔犗犖犌犗狊犲犾犲犮狋狊犮狅狉犲犳狉狅犿犛犜犝犇犈犖犜犛,犆犎犗犐犆犈犛狑犺犲狉犲犛犜犝犇犈犖犜犛.犵狉犪犱犲 = 2000犪狀犱犛犜犝犇犈犖犜犛.狊犻犱 =犆犎犗犐犆犈犛.狊犻犱犌犗

执行完毕得到执行计划:

|犕犲狉犵犲犑狅犻狀(犐狀狀犲狉犑狅犻狀…)

  |犆犾狌狊狋犲狉犲犱犐狀犱犲狓犛犮犪狀(…)

  |犆犾狌狊狋犲狉犲犱犐狀犱犲狓犛犮犪狀(…)

(7)在有聚簇索引条件下执行该连接查询,并比较效率,语句如下(见7_7_4.sql):

犝犛犈犛犮犺狅狅犾犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犛犈犜犛犎犗犠犘犔犃犖_犃犔犔犗犖犌犗狊犲犾犲犮狋狊犮狅狉犲犳狉狅犿犛犜犝犇犈犖犜犛,犆犎犗犐犆犈犛狑犺犲狉犲犛犜犝犇犈犖犜犛.犵狉犪犱犲 = 2000犪狀犱犛犜犝犇犈犖犜犛.狊犻犱 =犆犎犗犐犆犈犛.狊犻犱犌犗

执行得到结果:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读1945次,物理读0次,预读0次。

Page 209: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

202  数据库系统实验指导教程

表 '犛犜犝犇犈犖犜犛'。扫描计数1,逻辑读1033次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=282毫秒,耗费时间=357毫秒。

结果分析:

可以看到,在没有索引的情况下,系统默认使用哈希连接;在有聚簇索引(且有主外

键约束)的情况下,系统使用了归并连接。且在等值连接的情况下,哈希连接有着很高的

效率,甚至达到并超过了归并连接的效率。

5. 习题

(1)试一试如果上面实验CHOICES表中没有sid的主外键约束(但还是都有聚簇索

引),执行此连接查询时系统默认使用什么连接。(2)如果在CHOICES表的sid字段上没有索引(STUDENTS表sid上还是有聚簇

索引),或者只有非聚簇索引,在执行以上连接查询时系统的执行计划和执行效率如何?

实验7.8 填充因子

1. 实验目的

  理解填充因子的作用,并会根据实际情况设置合适的填充因子。

2. 原理解析

如果SQLServer数据库要经历大量的插入活动,那么很重要的一点是进行计划,以

便在索引页和数据页上提供和维持开放空间,防止出现页拆分。当某个索引页或数据页

不再能容纳任何新的行,但由于该页中所定义的数据的逻辑顺序需要插入一行时,便会发

生页拆分。发生页拆分时,SQLServer需要分割整页中的数据,并将大约一半数据移动

到新的页,以使这两页均有一些开放空间。这会消耗一些系统资源和时间。

当最初生成索引时,SQLServer将索引B树结构放置在连续的物理页上,以便通过

连续I/O扫描索引页获取最佳I/O性能。当由于发生页拆分,需要将新的页插入索引的

逻辑B树结构时,SQLServer必须分配新的8KB索引页。这种插入发生在硬盘上的其

他位置,从而打断了索引页的物理连续特性。它使I/O 操作从连续变为不连续,从而使

得性能减低一半。可以通过重建索引页以恢复索引页的物理连续顺序来解决过多的页拆

分。聚簇索引的页级也会遇到相同的问题,从而影响表的数据页。

有个办法可以比较有效地解决页拆分的问题:在建立索引页面时给每个页面留下一

些空间,方便以后插入数据,然后适时地在空闲时间重组索引等。这就是 填 充 因 子 的 作

用。聚簇索引的填充因子将会影响到数据页。对于非聚簇索引,填充因子会影响到索引

的叶节点,但不会影响到任何中间节点。如果要将填充因子应用到索引的中间节点,就需

要将pad_index选项设置为TRUE。

Page 210: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

203  第7章 物理优化

SQLServer数据库中CREATEINDEX命令中的FILLFACTOR表示填充因子,指定在索引页和数据页中要留出的开放空间的百分比。CREATEINDEX的PAD_INDEX选项在非页级索引页中应用 FILLFACTOR所指定的百分比。如果没 有 PAD_INDEX选项,FILLFACTOR 将 主 要 影 响 聚 簇 索 引 的 页 级 索 引 页。将 PAD_INDEX 选 项 与

FILLFACTOR一起使用是一个不错的主意。但是在这种情况下也需要进行抉择,以便找到一种既在页中留出足够的开放空间又

适当产生较少页拆分的性能折中方案。因为如果为 FILLFACTOR指定一 个 小 的 百 分

比,它将在索引页和数据页中留出大量的开放空间。这有助于避免产生页拆分,但是却无

法通过将数据压缩到页中改善性能。索引页和数据页上的压缩数据越多,SQLServer运

行越快,因为如果数据页和索引页上的压缩数据越多,则通常情况下可以用更少 的 页 和

I/O操作提取更多的数据。将 FILLFACTOR指定 得 过 高 会 使 页 中 留 出 的 开 放 空 间 过

少,使得页溢出的速度过快,从而引起页拆分。最好的填充因子是基于预期的在两次索引重建之间会发生的数据修改的百分比,计

算时应当对每个表分别加以估算。如果在一个具有100000行表中发现增加了4000个

新行,并对索引的列进行了6000次更新,就说明标上的写操作所占的比例大约为10%。此时,就可以基于这个写操作的比例,再加上一些余量来计算合适的填充因子了。按照这

种方法,在上面的例子要求的情况下,就应当将填充因子设置为大约85%。

3. 实验内容

先创建一个TEST2表,字段为(ID,NAME,AGE,RENT),先往表里加入100000万

条数据,然后再建立一个TEST3表,字段和TEST2表一样,用于对比。在RENT字段上

建立填充因子置为100%非聚簇索引,再把TEST2表内的所有记录加入到TEST3表中,在RENT字段上建立40%的非聚簇索引。然后分别在两个表中分别插入100000条记

录。最后在两个表中分别执行同样查询,并查看执行速度,比较两次查询的效率差别。最

后再在TEST2表和TEST3表上重建索引,分别执行查询,比较效率。

4. 实验步骤

(1)首先打开SQL查询分析器。(2)建立TEST2表,执行如下语句(见7_8_1.sql):

犝犛犈犛犮犺狅狅犾犌犗犆犚犈犃犜犈犜犃犅犔犈犜犈犛犜2(

 犐犇犝犖犐犙犝犈犐犇犈犖犜犐犉犐犈犚犖犗犜犖犝犔犔

   犚犗犠犌犝犐犇犆犗犔犇犈犉犃犝犔犜(犖犈犠犐犇())

   犘犚犐犕犃犚犢犓犈犢犖犗犖犆犔犝犛犜犈犚犈犇,

 犖犃犕犈犞犃犚犆犎犃犚(50)犖犗犜犖犝犔犔,

 犃犌犈犐犖犜,

 犚犈犖犜犐犖犜犖犗犜犖犝犔犔)

Page 211: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

204  数据库系统实验指导教程

(3)插入100000条数据,执行如下语句(见7_8_2.sql):

犇犈犆犔犃犚犈@犐犐犖犜犛犈犜@犐=1犠犎犐犔犈@犐<=100000犅犈犌犐犖

 犐犖犛犈犚犜犐犖犜犗犜犈犛犜2(犖犃犕犈,犃犌犈,狉犲狀狋)

   犞犃犔犝犈犛(狀犲狑犻犱(),@犐,狉犪狀犱()10000000)

 犛犈犜@犐= @犐+1犈犖犇犌犗

(4)在RENT字段上建立填充因子为100%非聚簇索引,语句如下(见7_8_3.sql):

犆犚犈犃犜犈犐犖犇犈犡犐犡_犚犈犖犜

 犗犖犜犈犛犜2(犚犈犖犜)犠犐犜犎犉犐犔犔犉犃犆犜犗犚=100

 /建立填充因子为100%的聚簇索引/

犌犗

此时,在sysindexes系统表中查得索引页数为186页。(5)建立TEST3表,语句如下(见7_8_4.sql):

犝犛犈犛犮犺狅狅犾犌犗犆犚犈犃犜犈犜犃犅犔犈犜犈犛犜3(

 犐犇犝犖犐犙犝犈犐犇犈犖犜犐犉犐犈犚犖犗犜犖犝犔犔

   犚犗犠犌犝犐犇犆犗犔犇犈犉犃犝犔犜(犖犈犠犐犇())

   犘犚犐犕犃犚犢犓犈犢犖犗犖犆犔犝犛犜犈犚犈犇,

 犖犃犕犈犞犃犚犆犎犃犚(50)犖犗犜犖犝犔犔,

 犃犌犈犐犖犜,

 犚犈犖犜犐犖犜犖犗犜犖犝犔犔)

(6)将TEST2表中数据导入TEST3表,语句如下(见7_8_5.sql):

犐犖犛犈犚犜犐犖犜犗犜犈犛犜3犛犈犔犈犆犜犉犚犗犕犜犈犛犜2

(7)在TEST3表上建立填充因子为40%的非聚簇索引,语句如下(见7_8_6.sql):

犆犚犈犃犜犈犐犖犇犈犡犐犡_犚犈犖犜2

 犗犖犜犈犛犜3(犚犈犖犜)犠犐犜犎犉犐犔犔犉犃犆犜犗犚=40

 /建立填充因子为40%的聚簇索引/

犌犗

此时,在sysindexes系统表中查得索引页数为464页。(8)在TEST2表中插入100000条数据,语句如下(见7_8_7.sql):

犇犈犆犔犃犚犈@犐犐犖犜犛犈犜@犐=100001

Page 212: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

205  第7章 物理优化

犠犎犐犔犈@犐<=200000犅犈犌犐犖 犐犖犛犈犚犜犐犖犜犗犜犈犛犜2(犖犃犕犈,犃犌犈,狉犲狀狋)

   犞犃犔犝犈犛(狀犲狑犻犱(),@犐,狉犪狀犱()10000000)

 犛犈犜@犐= @犐+1犈犖犇犌犗

此时在sysindexes系统表中查得此索引所占页数为545页。(9)在TEST3表中插入100000条数据,语句如下(见7_8_8.sql):

犇犈犆犔犃犚犈@犐犐犖犜犛犈犜@犐=100001犠犎犐犔犈@犐<=200000犅犈犌犐犖 犐犖犛犈犚犜犐犖犜犗犜犈犛犜3(犖犃犕犈,犃犌犈,狉犲狀狋)

   犞犃犔犝犈犛(狀犲狑犻犱(),@犐,狉犪狀犱()10000000)

 犛犈犜@犐= @犐+1犈犖犇犌犗

此时在sysindexes系统表中查得此索引所占页数为550页。(10)在TEST2表中执行查询,语句如下(见7_8_9.sql):

犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犛犈犔犈犆犜犉犚犗犕犜犈犛犜2犠犎犈犚犈犚犈犖犜犅犈犜犠犈犈犖5980000犃犖犇6000000犌犗

执行完毕得到结果:

(所影响的行数为388行)

表 '犜犈犛犜2'。扫描计数1,逻辑读392次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间 =0毫秒,耗费时间 =19毫秒。

(11)在TEST3表中执行查询,语句如下(见7_8_10.sql):

犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犛犈犔犈犆犜犉犚犗犕犜犈犛犜3犠犎犈犚犈犚犈犖犜犅犈犜犠犈犈犖5980000犃犖犇6000000

Page 213: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

206  数据库系统实验指导教程

犌犗

执行完毕得到结果:

(所影响的行数为392行)

表 '犜犈犛犜3'。扫描计数1,逻辑读396次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间 =0毫秒,耗费时间 =12毫秒。

(12)在TEST2表上重建索引,语句如下(见7_8_11.sql):

犇犚犗犘犐犖犇犈犡犜犈犛犜2.犐犡_犚犈犖犜犌犗犆犚犈犃犜犈犐犖犇犈犡犐犡_犚犈犖犜

 犗犖犜犈犛犜2(犚犈犖犜)犠犐犜犎犉犐犔犔犉犃犆犜犗犚=100犌犗

此时,在sysindexes系统表中查得索引页数为372页。(13)在TEST3表上重建索引,语句如下(见7_8_12.sql):

犇犚犗犘犐犖犇犈犡犜犈犛犜3.犐犡_犚犈犖犜2犌犗犆犚犈犃犜犈犐犖犇犈犡犐犡_犚犈犖犜2

 犗犖犜犈犛犜3(犚犈犖犜)犠犐犜犎犉犐犔犔犉犃犆犜犗犚=100犌犗

此时,在sysindexes系统表中查得索引页数为927页。(14)在TEST2表上执行查询,语句如下(见7_8_13.sql):

犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犛犈犔犈犆犜犉犚犗犕犜犈犛犜2犠犎犈犚犈犚犈犖犜犅犈犜犠犈犈犖5980000犃犖犇6000000犌犗

执行完毕得到结果:

(所影响的行数为388行)

表 '犜犈犛犜2'。扫描计数1,逻辑读390次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=0毫秒,耗费时间=10毫秒。

(15)在TEST3表上执行查询,语句如下(见7_8_14.sql):

犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗

Page 214: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

207  第7章 物理优化

犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖

犌犗

犛犈犔犈犆犜犉犚犗犕犜犈犛犜2犠犎犈犚犈犚犈犖犜犅犈犜犠犈犈犖5980000犃犖犇6000000

犌犗

执行完毕得到结果:

(所影响的行数为392行)

表 '犜犈犛犜3'。扫描计数1,逻辑读396次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=0毫秒,耗费时间=24毫秒。

结果分析:

可以看到填充因子为100%的那个索引因为连续的插 入 索 引 页 数 大 大 增 加(从186增加到545),而填充因子为40%那个索引却没有因为连续的插入导致索引页数的急剧增

加(还是464,说 明 没 有 造 成 分 页)。效 率 上 同 样 是200000条 记 录,重 建 索 引 后 因 为

TEST2表的索引页数少于TEST3表(因为填充率高),导致TEST2表用到此索引的查

询快于TEST3表;但是没有重建索引时,因为TEST2表的索引填充率高,插入导致大量

分页,索引页数急剧增加,而TEST3表的索引没有什么分页,故性能没有大幅下降,这时

用到此索引的查询变得反而比TEST2表快了。TEST3表重 建 索 引 后 查 询 变 慢 是 因 为

重建时要保证40%的填充率,使得索引页数增加,说明过小的填充因子会导致查询效率

的减慢。在此各种情况下,用时差别虽然只有几毫秒,但是当数据量很大时,效率的差距

就会很明显了。

5. 习题

(1)自己重复做一次上面的实验,把查询改为SELECTFROMTEST2WHERERENTBETWEEN5000000AND10000000,看看两种填充因子的索引在这个查询中效

率的不同,为什么?

(2)用聚簇索引做一次上面的实验,看看有什么异同。

实验7.9 组合索引

1. 实验目的

  通过实验了解组合索引的作用,明白什么情况下系统要使用组合索引。

2. 原理解析

组合索引就是指建立在多个属性上的索引。组合索引可以是聚簇的,也可以是非聚

簇的。例如:电话簿里的索引(姓,名)正是一个组合索引。组合索引(A,B,…)如果是聚

Page 215: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

208  数据库系统实验指导教程

簇的,则先按照A的值划分,A值相邻的存放也相邻,在同一个A值的记录集合内(这些

记录是挨着存放的),按照B的值划分存放,B值相邻的存放也相邻,相同B值的记录也挨

着存放,即:此时实际上是(A,B)值都相同,以此类推。比较在单个属性上建立的索引,组合索引具有以下优势:(1)支持前缀匹配查询,支持的前缀就是组合索引(A,B,…)的形式。(2)更易覆盖查询条件,有时一个稠密的组合索引就可以完全回答查询。例如查姓

为“罗”,名为“强”的人有多少个。(3)组合索引是支持多属性唯一性的一个有效办法,比如,在上面的电话簿例子中,

如果不允许姓和名都相等的记录存在,则可以使用SQL可选项 UNIQUE建立一个基于

(姓,名)的唯一的组合索引,这样是很有效率的。在设计一个组合索引时,必须注意组合索引的顺序,如果查询更倾向于在属性 A而

不是在属性B上加限定词的话,那么应该建立把A放在B前面的组合索引。组合索引也有如下缺点:(1)组合索引趋向于比较长的索引键。在前面B树中已经讨论过了,如果不使用压

缩方法,这会引起B树层数的增加。虽然使用哈希结构来实现组合索引可以解决索引键

大小问题,但是这样就不支持前缀匹配查询(故一般系统都用B树来实现组合索引)。(2)因为组合索引包含多个属性,所以对其中任何属性的更新都会导致索引的更新,

这样,组合索引的维护代价将会是比较高的。

3. 实验内容

在数据库CHOICES表上建立非聚簇的组合索引(sid,tid),执 行 查 询selectsid,tidfromchoiceswheresid=823069829andtidbetween220000000and250000000;再删掉

此索引,重新建立组合索引(tid,sid),再次执行查询;最后删掉索引执行查询。分别比较

三次查询的效率。

4. 实验步骤

(1)首先打开SQL查询分析器。(2)建立组合索引(sid,tid),语句如下(见7_9_1.sql):

犝犛犈犛犮犺狅狅犾犌犗犆犚犈犃犜犈犐犖犇犈犡犐犡_犛犐犇_犜犐犇犗犖犆犎犗犐犆犈犛(犛犐犇,犜犐犇)

犌犗

(3)执行查询,语句如下(见7_9_2.sql):

犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗狊犲犾犲犮狋狊犻犱,狋犻犱犳狉狅犿犮犺狅犻犮犲狊狑犺犲狉犲狊犻犱=823069829犪狀犱狋犻犱犫犲狋狑犲犲狀220000000犪狀犱250000000

Page 216: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

209  第7章 物理优化

犌犗

执行结束得到结果:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读1262次,物理读0次,预读1165次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间 =125毫秒,耗费时间 =622毫秒。

查看查询计划:为犐狀犱犲狓犛犮犪狀。

(4)删去原组合索引,重建组合索引(tid,sid),语句如下(见7_9_3.sql):

犇犚犗犘犐犖犇犈犡犆犎犗犐犆犈犛.犐犡_犛犐犇_犜犐犇犌犗犆犚犈犃犜犈犐犖犇犈犡犐犡_犜犐犇_犛犐犇犗犖犆犎犗犐犆犈犛(犜犐犇,犛犐犇)

犌犗

(5)执行查询,语句如下(见7_9_4.sql):

犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗狊犲犾犲犮狋狊犻犱,狋犻犱犳狉狅犿犮犺狅犻犮犲狊狑犺犲狉犲狊犻犱=823069829犪狀犱狋犻犱犫犲狋狑犲犲狀220000000犪狀犱250000000犌犗

执行结束得到结果:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读1261次,物理读0次,预读1146次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间 =110毫秒,耗费时间 =684毫秒。

查看查询计划:为犐狀犱犲狓犛犮犪狀。

(6)删去组合索引,语句如下(见7_9_5.sql):

犇犚犗犘犐犖犇犈犡犆犎犗犐犆犈犛.犐犡_犜犐犇_犛犐犇犌犗

(7)执行查询,语句如下(见7_9_6.sql):

犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗狊犲犾犲犮狋狊犻犱,狋犻犱犳狉狅犿犮犺狅犻犮犲狊狑犺犲狉犲狊犻犱=823069829犪狀犱狋犻犱犫犲狋狑犲犲狀220000000犪狀犱250000000犌犗

执行结束得到结果:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读1742次,物理读0次,预读1750次。

Page 217: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

210  数据库系统实验指导教程

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=125毫秒,耗费时间=1467毫秒。

查看查询计划:为犜犪犫犾犲犛犮犪狀。

结果分析:两个组合索引均为IndexScan,但时间有所差别,因为第一个组合索引为(sid,tid),

查询里顺序和索引顺序一致,且sid上为点查询,tid为范围查询,故索引扫描只扫描了sid为823069829的所有索引节点;而组合索引为(tid,sid)时,查询中tid为范围查询,索引

匹配停在了第一个范围查询后,即索引只匹配了tid,没有用到sid,故索引扫描为全部索

引节点扫描,因此比前一个慢。但是这两个均为覆盖查询,不需访问数据页面,而第三个

查询是表扫描,要扫描数据页面,所以比前两个慢。

习题答案

实验 7.1

   犝犛犈犛犮犺狅狅犾犌犗犆犚犈犃犜犈犐犖犇犈犡犐犡_犲犿犪犻犾犗犖犛犜犝犇犈犖犜犛(犈犕犃犐犔)

犇犈犆犔犃犚犈@狀犪犿犲犞犃犚犆犎犃犚(20)

犛犈犔犈犆犜@狀犪犿犲=狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊犠犎犈犚犈犻犱=狅犫犼犲犮狋_犻犱('犛犜犝犇犈犖犜犛')

犈犡犈犆('犪犾狋犲狉狋犪犫犾犲犛犜犝犇犈犖犜犛犱狉狅狆犮狅狀狊狋狉犪犻狀狋'+@狀犪犿犲)

犌犗犃犔犜犈犚犜犃犅犔犈犛犜犝犇犈犖犜犛犠犐犜犎犖犗犆犎犈犆犓犃犇犇

 犆犗犖犛犜犚犃犐犖犜[犘犓_犛犐犇]犘犚犐犕犃犚犢犓犈犢犖犗犖犆犔犝犛犜犈犚犈犇([犛犐犇])

犌犗犆犚犈犃犜犈犆犔犝犛犜犈犚犈犇犐犖犇犈犡犐犡_犛犖犃犕犈犗犖犜犈犛犜(犛犖犃犕犈)

犌犗

实验 7.2

先记录下没有索引时的查询效率:

犝犛犈犛犮犺狅狅犾犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犛犈犔犈犆犜犆犐犇犉犚犗犕犆犎犗犐犆犈犛犠犎犈犚犈犛犐犇犔犐犓犈'846806971'

犌犗

执行完得到结果:

Page 218: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

211  第7章 物理优化

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读6次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间 =0毫秒,耗费时间 =18毫秒。

在sid和cid上建立组合索引:

犆犚犈犃犜犈犐犖犇犈犡犐犡_犛犐犇_犆犐犇犗犖犆犎犗犐犆犈犛(犛犐犇,犆犐犇)

犌犗

再执行查询:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读3次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=0毫秒,耗费时间=0毫秒。

可以看到读盘次数和用时的效率都有不少提高。

实验 7.3

(1)先建立一个表TEST_INSERT:

犝犛犈犛犮犺狅狅犾犌犗犆犚犈犃犜犈犜犃犅犔犈犜犈犛犜_犐犖犛犈犚犜(

 犐犇犝犖犐犙犝犈犐犇犈犖犜犐犉犐犈犚犖犗犜犖犝犔犔

   犚犗犠犌犝犐犇犆犗犔犇犈犉犃犝犔犜(犖犈犠犐犇())

   犘犚犐犕犃犚犢犓犈犢犖犗犖犆犔犝犛犜犈犚犈犇,

 犖犃犕犈犞犃犚犆犎犃犚(50)犖犗犜犖犝犔犔,

 犃犌犈犐犖犜,

 犚犈犖犜犐犖犜犖犗犜犖犝犔犔)

(2)往表中加入100000条记录:

犇犈犆犔犃犚犈@犐犐犖犜犛犈犜@犐=1犠犎犐犔犈@犐<=100000犅犈犌犐犖

 犐犖犛犈犚犜犐犖犜犗犜犈犛犜_犐犖犛犈犚犜(犖犃犕犈,犃犌犈,狉犲狀狋)

   犞犃犔犝犈犛(狀犲狑犻犱(),@犐,狉犪狀犱()10000000)

 犛犈犜@犐= @犐+1犈犖犇犌犗

最后得到用时为4分15秒。(3)删除所有记录,添加一个非聚簇索引:

Page 219: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

212  数据库系统实验指导教程

犇犈犔犈犜犈犉犚犗犕犜犈犛犜_犐犖犛犈犚犜

犌犗

犆犚犈犃犜犈犐犖犇犈犡犐犡_犖犃犕犈犗犖犜犈犛犜_犐犖犛犈犚犜(犖犃犕犈)

犌犗

(4)向表中加入100000条记录:

犇犈犆犔犃犚犈@犐犐犖犜

犛犈犜@犐=1

犠犎犐犔犈@犐<=100000

犅犈犌犐犖

 犐犖犛犈犚犜犐犖犜犗犜犈犛犜_犐犖犛犈犚犜(犖犃犕犈,犃犌犈,狉犲狀狋)

   犞犃犔犝犈犛(狀犲狑犻犱(),@犐,狉犪狀犱()10000000)

 犛犈犜@犐= @犐+1

犈犖犇

犌犗

最后得到用时为4分52秒。(5)删除所有记录,更改非聚簇索引为聚簇索引:

犇犈犔犈犜犈犉犚犗犕犜犈犛犜_犐犖犛犈犚犜

犌犗

犇犚犗犘犐犖犇犈犡犜犈犛犜_犐犖犛犈犚犜.犐犡_犖犃犕犈

犌犗

犆犚犈犃犜犈犆犔犝犛犜犈犚犈犇犐犖犇犈犡犐犡_犖犃犕犈犗犖犜犈犛犜_犐犖犛犈犚犜(犖犃犕犈)

犌犗

(6)向表中插入100000条记录:

犇犈犆犔犃犚犈@犐犐犖犜

犛犈犜@犐=1

犠犎犐犔犈@犐<=100000

犅犈犌犐犖

 犐犖犛犈犚犜犐犖犜犗犜犈犛犜_犐犖犛犈犚犜(犖犃犕犈,犃犌犈,狉犲狀狋)

   犞犃犔犝犈犛(狀犲狑犻犱(),@犐,狉犪狀犱()10000000)

 犛犈犜@犐= @犐+1

犈犖犇

犌犗

最后得到用时为5分21秒。

结果分析:

没有索引时插入数据最快,有非聚簇索引时插入慢些,因为要修改索引,而有聚簇索

引时最慢,因为数据插入前要根据索引找到插入的位置,有可能还要把其他数据移位,插

入后要修改索引(但是在并行状况下,没有索引或有非聚簇索引的插入可能会造成争锁而

延迟,对性能影响大,因为这两种都是在随后数据页面插入数据。而聚簇索引则不会)。

Page 220: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

213  第7章 物理优化

实验 7.4

(1)建立非聚簇索引:

犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗犝犛犈犛犆犎犗犗犔犆犚犈犃犜犈犐犖犇犈犡犐犡_犛犆犗犚犈犗犖犆犎犗犐犆犈犛(犛犆犗犚犈)

犌犗

执行结束得到用时:

表 '犆犎犗犐犆犈犛'。扫描计数2,逻辑读4948次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=1641毫秒,耗费时间=2393毫秒。

(2)表扫描更新数据:

犝犘犇犃犜犈犆犎犗犐犆犈犛犛犈犜犛犆犗犚犈=犛犆犗犚犈+1犠犎犈犚犈犛犆犗犚犈<60犌犗

执行结束得到用时:

表 '犆犎犗犐犆犈犛'。扫描计数36045,逻辑读294881次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=1531毫秒,耗费时间=2709毫秒。

(3)点查询(用索引)更新数据:

犝犘犇犃犜犈犆犎犗犐犆犈犛犛犈犜犛犆犗犚犈=120犠犎犈犚犈犛犆犗犚犈=60犌犗

执行结束得到用时:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读42385次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=141毫秒,耗费时间=162毫秒。

(4)删除索引:

犇犚犗犘犐犖犇犈犡犆犎犗犐犆犈.犐犡_犛犆犗犚犈犌犗

执行完得到用时:

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=1毫秒,耗费时间=1毫秒。

(5)表扫描更新数据:

Page 221: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

214  数据库系统实验指导教程

犝犘犇犃犜犈犆犎犗犐犆犈犛犛犈犜犛犆犗犚犈=犛犆犗犚犈+1犠犎犈犚犈犛犆犗犚犈<60犌犗

执行结束得到用时:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读38517次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=249毫秒,耗费时间=249毫秒。

(6)点查询(表扫描)更新数据:

犝犘犇犃犜犈犆犎犗犐犆犈犛犛犈犜犛犆犗犚犈=60犠犎犈犚犈犛犆犗犚犈=120犌犗

执行结束得到用时:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读8503次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=203毫秒,耗费时间=209毫秒。

结果分析:说明有非聚簇索引时表扫描更新变慢,这是因为要更新索引,而 用 到 此 索 引 的 更 新

(比如点查询)则变快,因为虽然也需要更新索引,但是系统可以用此索引找到该记录,不

用表扫描。

实验 7.5

没有性能提升,因为score最 多100个 值,而 计 算 知 一 个 数 据 页 有 大 约200来 条 记

录,也就是说每个数据页里平均都会有两个不同的score值,所以不管什么查询都是表扫

描了,故系统不会用score上的非聚簇索引。同理,cid只有50个不同值。

实验 7.6

(1)先建立索引:

犝犛犈犛犮犺狅狅犾犐犉犖犗犜犈犡犐犛犜犛(犛犈犔犈犆犜狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊犠犎犈犚犈狀犪犿犲= '犻狓_狊犻犱')

犆犚犈犃犜犈犐犖犇犈犡犻狓_狊犻犱犗犖犆犎犗犐犆犈犛(狊犻犱)

犌犗犐犉犖犗犜犈犡犐犛犜犛(犛犈犔犈犆犜狀犪犿犲犉犚犗犕狊狔狊犻狀犱犲狓犲狊犠犎犈犚犈狀犪犿犲= '犻狓_狋犻犱')

犆犚犈犃犜犈犐犖犇犈犡犻狓_狋犻犱犗犖犆犎犗犐犆犈犛(狋犻犱)

犌犗

(2)执行查询,查看执行计划:

犛犈犜犛犎犗犠犘犔犃犖_犃犔犔犗犖犌犗

Page 222: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

215  第7章 物理优化

犛犈犜犛犜犃犜犐犛犜犐犆犛犐犗犗犖犌犗犛犈犜犛犜犃犜犐犛犜犐犆犛犜犐犕犈犗犖犌犗狊犲犾犲犮狋犳狉狅犿犆犎犗犐犆犈犛狑犺犲狉犲狊犻犱犫犲狋狑犲犲狀'880000000'犪狀犱 '889000000'犪狀犱狋犻犱犫犲狋狑犲犲狀 '229850000'

犪狀犱'229900000'

犌犗

执行完得到结果:

|犉犻犾狋犲狉(犠犎犈犚犈:([犆犎犗犐犆犈犛].[狊犻犱]…)

   |犐狀犱犲狓犛犲犲犽(犗犅犑犈犆犜:([犛犮犺狅狅犾].[犱犫狅].[犆犎犗犐犆犈犛].[犻狓_狋犻犱])…

这里说明用了ix_tid这个索引,而没有用sid索引。因为880000000~889000000范围过大,等同于表扫描。故系统用此条件做过滤谓词。

实验 7.7

(1)在没有主外键约束情况下,这里系统使用的是哈希连接。(2)CHOICES表sid上没有索引时,执行得到:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读2474次,物理读0次,预读0次。

表 '犛犜犝犇犈犖犜犛'。扫描计数1,逻辑读1487次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=47毫秒,耗费时间=476毫秒。

且查看查询计划知:

|犎犪狊犺犕犪狋犮犺(犐狀狀犲狉犑狅犻狀…)

   |犆犾狌狊狋犲狉犲犱犐狀犱犲狓犛犮犪狀(…)

   |犜犪犫犾犲犛犮犪狀(…)

犛犻犱上有非聚簇索引时,执行得到:

表 '犆犎犗犐犆犈犛'。扫描计数1,逻辑读2474次,物理读0次,预读0次。

表 '犛犜犝犇犈犖犜犛'。扫描计数1,逻辑读1487次,物理读0次,预读0次。

犛犙犔犛犲狉狏犲狉执行时间:

 犆犘犝时间=31毫秒,耗费时间=452毫秒。

|犎犪狊犺犕犪狋犮犺(犐狀狀犲狉犑狅犻狀…)

   |犆犾狌狊狋犲狉犲犱犐狀犱犲狓犛犮犪狀(…)

   |犜犪犫犾犲犛犮犪狀(…)

这里说明CHOICES表sid有非聚簇索引或没有索引时都是TableScan,且都用的哈

希连接。效率没有sid上建立聚簇索引时高。

实验 7.8

(2)两种填充因子皆不起作用,效率一样,因为这里系统采用的是TableScan,没有

用到索引。

Page 223: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

附录A 实验数据环境说明

本书的实验根据School数据库展开,本附录对这个数据库的表格进行说明。

School数据库由四个表格组成,如表A1~A4所示,描述一 个 学 校 的 学 生、教 师、课

程和选课关系。

表A1 STUDENTS表格(记录了学生的基本信息)

字  段 说  明

sid  学生的唯一性标识,主键,char(10)

sname  学生姓名,非空,char(30)

email  学生的email,char(30)

grade  学生所在年级,如“2001”,int

表A2 TEACHERS表格(记录教师的基本信息)

字  段 说  明

tid  教师的唯一性标识,主键,char(10)

tname  教师姓名,非空,char(30)

email  教师的email,char(30)

salary  教师工资,单位“元”,如“3200”,int

表A3 COURSES表格(记录可供选择课程的信息)

字  段 说  明

cid  课程的唯一性标识,主键,char(10)

cname  课程名称,非空,char(30)

hour  需要讲授的小时数,如“48”,int

表A4 CHOICES表格(记录了选课关系,即某个学生sid选择了

由某个老师tid开设的某门课程cid)

字  段 说  明

no 选课关系的唯一性标识,主键,intsid 选课学生标识,非空,外键引用STUDENTS表格sid,char(10)

tid 授课老师标识,外键引用TEACHERS表格tid,char(10)

cid 选中课程标识,非空,外键引用COURSES表格cid,char(10)

score 学生本门课程的分数,int

  注:no是这个表的主键,另外包含了来自STUDENTS、TEACHERS、COURSES三个表格的外键。

Page 224: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

附录B 实验环境构建指南

为了使读者可以更好地利用本书提供的环境进行实验,本附录介绍实验环境的构建。

主要从以下两个方面进行说明:

MicrosoftSQLServer2000的安装;

实验数据(School数据库)的导入。

【MicrosoftSQLServer2000的安装】

SQLServer2000是 Microsoft的一个数据库服务器软件。它由两个部分组成:服务

器组件和客户端工具。SQLServer2000的核心是服务器组件,它们提供数据库的服务;

而用户直接接触的是客户端工具。服务器组件是引擎,客户端工具是用户界面,两者是相

辅相成的。

安装SQLServer2000实际上就是安装服务器组件和客户端工具。当然,也可以选

择同时安装服务器组件和客户端工具,或者只安装其中的一个,甚至只选择安装更少的东

西。另外,SQLServer2000有很多版本:企业版、开发版、标准版、个人版等。每一个版

本包含的客户端工具基本上是一样的,而服务器组件可能有些不同。下面对其安装过程

进行说明。

SQLServer2000的安装程序是非常智能化的,基本上用户只需要跟着提示,选择默

认项。下面说明大致过程,并在几处关键地方进行讲解。

(1)插入安装盘,运行光盘上的自动播放(手动或自动运行)。在弹出的界面中选择

安装SQLServer2000对应版本,就可以开始安装。

(2)在第一步的选择中,选择安装在本地计算机上。

(3)在下一步中,选择创建一个新的实例。

(4)在下一步的输入框中,输入用户信息。

(5)在下一步中,注意要选择“服务器和客户端工具”,如图B1所示。

图B1 选择“服务器和客户端工具”

因为本书要 在 本 地 计 算 机 上 进 行 数 据 库 服 务,所

以这里不能选择其余选项。

(6)选择默认的实例名称。

(7)接着选择安装的路径。

(8)定制安装的具体组件,建议采用默认设置。

(9)设置服务启动方式,建议统一以“本地系统账

户”启动它们,如图B2所示。

Page 225: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

218  数据库系统实验指导教程

图B2 设置服务启动方式

  (10)设置身份验证模式,如图B3所示。这里可以选择 Windows身 份 验 证 模 式,也 可 以 选 择 混 合 模 式。只 要 此 后 使 用 的 时

候,采用与这里设置的模式符合的模式登录服务器即可。(11)在接着的两个对话框中,选择默认的校验方式和网络配备。(12)在确认对话框中确认,开始安装。(13)安装之后,按照要求(如果有)重启计算机。重新启动计算机之后,可以运行安装之后的SQLServer2000中的“服务管理器”,然

后开始运行本机上的服务器,如图B4所示的正常启动情形,说明服务端组件已经正常安

装了。

图B3 设置身份验证模式 图B4 正常启动

另外,还可以打开“企业管理器”,展开数据库本机SQLServer实例节点的数据库项

图B5 在企业管理器中观看数据库项目

目,可以 发 现SQLServer已 经 自 动 安 装 了 六 个

数据库,如图B5所示。

【实验数据(School数据库)的导入】

这 里 介 绍 如 何 将 本 书 提 供 的 实 验 数 据

School数据库导入到安装好的SQLServer2000

Page 226: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

219  附录B 实验环境构建指南

中,导入的School数据库可以在如图B5的图中观察到。(1)复制网站下载的“实验数据”文件夹下的两个文件School_Data.MDF(数据文件)

和School_Log.LDF(日志文件)到SQLServer2000安装目录下的 MSSQL文件夹下的

Data中。(2)进入“企业管理器”中。展开“控制台根目录”到“数据库”一级,并在“数据库”上

单击右键,选择“所有任务”,然后选择“附加数据库”。在弹出的对话框中选择刚刚放入

SQLServer2000安装目录的 MDF文件,然后单击“确定”按钮,便可以导入数据库。此

时可以在数据库目录下看到新添加的School数据库,说明导入成功。读者还可以查看数

据库中的表格,以加深对实验数据结构的理解。

Page 227: idl.hbdlib.cnidl.hbdlib.cn/book/00000000000000/pdfbook2/018/017/249077.pdf · 书 SQL语言 C H A P T E R 1 第1章 SQL是StructuredQueryLanguage的缩写,译成“结构化查询语言”,

读者意见反馈

亲爱的读者:感谢您一直以来对清华版计算机教材的支持和爱护。为了今后给您提供更优秀的教

材,请您抽出宝贵的时间来填写下面的意见反馈表,以便于我们更好地对本教材做进一

步改进。同时如果您在使用本教材的过程中遇到了什么问题,或者有什么好的建议,也

请来信告诉我们。地址:北京市海淀区双清路学研大厦A座517 (100084) 市场部收

电话:627701754608电子邮件:jsjjc@tuptsinghuaeducn

教材名称:数据库系统实验指导教程

ISBN:7302125600/TP·8035个人资料

姓名:  年龄: 所在院校/专业:文化程度:  通信地址:联系电话:  电子信箱:您使用本书是作为:□指定教材 □选用教材 □辅导教材 □自学教材

您对本书封面设计的满意度:

□很满意 □满意 □一般 □不满意 改进建议

您对本书印刷质量的满意度:

□很满意 □满意 □一般 □不满意 改进建议

您对本书的总体满意度:从语言质量角度看 □很满意 □满意 □一般 □不满意

从科技含量角度看 □很满意 □满意 □一般 □不满意

本书最令您满意的是:

□指导明确 □内容充实 □讲解详尽 □实例丰富

您认为本书在哪些地方应进行修改?(可附页)

您希望本书在哪些方面进行改进?(可附页)

电子教案支持

敬爱的教师:为了配合本课程的教学需要,本教材配有配套的电子教案,有需求的教师可以与我

们联系,我们将向使用本教材进行教学的教师免费赠送电子教案,希望有助于教学活动

的开展。相 关 信 息 请 拨 打 电 话627701754608或 发 送 电 子 邮 件 至jsjjc@tup.tsinghua.edu.cn咨询,也可以到清华大学出版社主页(http://www.tup.com.cn)上查询。