瑞星卡卡安全论坛技术交流区系统软件 【推荐】SQL 21 日自学通-2

12345   4  /  5  页   跳转

【推荐】SQL 21 日自学通-2

第12 天数据库安全
今天我们来讨论一下数据库的安全问题我们已经很清楚地看到不同的SQL 语句可以
使我们具有管理关系型数据库系统的能力与我们到今天为止所学习的其它主题一样一
个数据库管理系统是如何在不同的产品中实现安全的呢对于这个问题我们今天将以流行
的ORACLE 7 数据库系统为例到今天的结束时我们将具有以下能力
l 创建用户
l 更改密码
l 创建角色
l 为安全的目的而使用视图
l 在视图中使用同义词
前提数据库管理员
安全问题在数据库的设计过程中常常会被忽略许多计算机工作人员在进入计算机领
域时有计算机的编程知识或硬件知识并且他们也会将精力注重于这一方面例如如果
你的老板要求你开展一个新的项目而这个项目很明显地需要一个关系型的数据库时那
你第一步想要做的工作是什么在确定的相应的硬件和软件平台以后你也许会开始设计
项目中的基本的数据库结构了这一阶段会分给许多人去做其中一些人是图形用户界面
设计者另一些底层组件的设计者也许你在读过本书以后会要求编写用于提供给客户应
用程序所使用查询的SQL 代码而与这项任务随之而来的是你可能会成为一个数据库的管
理和维护人员
在很多时候当我们在真正实施这个项目时我们需要考虑或计划到一点那就是当众
多的用户通过广域网使用你的应用程序时会有什么情况发生根据今天个人计算机的强大
软件和硬件以及微软的开放数据库联接ODBC 任何连接于网络的用户都会有办法得到
你的数据库我们不想在当我们公司的计算机准备联入internet 时或类似的大型网络时有
麻烦出现那么我们应该如何面对这一情况
非常幸运软件供应商已经为你处理安全问题提供了许多的工具每一个新的网络操
作系统都会面对着比它的上一代更为严格的安全性要求此外许多的数据库供应商也都
SQL 21 日自学通(V1.0) 翻译人笨猪
239
在它们的数据库系统提供了不同程度的与你的网络操作系统安全相独立的安全性所以
想在不同的产品中实现安全性的方法是非常广泛的
流行的数据库产品与安全
就像你所知道的那样许多数据库管理系统为你的生意而存在着商业竞争在一个项
目的开发过程中你可能会只购买少数的许可证用以测试开发以及其它的目的但是
你的产品的实际许可证要求可能会是成百上千个此外当你决定采用某一种数据库管理
系统时你可能会在这个产品中渡过几年的时间所以当你在检查下边的数据库管理系
统时头脑中要有下边的概念
MicroSoft FoxPro 数据库管理系统是一个非常强大的基于单用户环境的数据库管理系
统它只使用了有限的SQL 标准的子集在该数据库系统中没有提供安全性措施同时
它使用了Xbase 的文件格式每一个文件中都只有一个表索引文件存储于单独的表中
MicroSoft Access 数据库管理系统提供了更多的SQL 实现尽管它内部已经包括了基
本的安全系统但它仍然是一个基于PC 平台的数据库管理系统该数据库系统允许你创
建查询并把它们存储在数据库之中此外全部的数据库及其对象均存在于同一个文件之

Oracle 7 数据库管理系统支持全部的标准的SQL 此外它还对标准的SQL 进行了称
之为PL*SQL 的扩充它拥有全部的安全特性包括在数据库中创建角色以及为数据库对
象分配权限的能力
Sybase SQL 拥有与Oracle 7 类似的能力与特性它也提供了极大范围内的安全特性
它对SQL 的扩充被称为Transact-SQL
对这些产品进行描述的目的是想说明并不是所有的软件都适用于每一个应用程序如
果你的程序用于商业目的那么你的选择将会受到限制成本与性能的因素是非常重要的
然而如果没有足够的安全手段那么任何在创建数据库之初时的费用节约都将被安全问
题吃掉
如何让一个数据库变得安全
到现在为止你还没有因为数据库的安全问题而不高兴但是否你曾经因为你不想让其
它的用户登录进入你的数据库系统造成破坏而不得不非常小心的登录如果有一天早上你
SQL 21 日自学通(V1.0) 翻译人笨猪
240
登录进行系统后发现你辛苦努力的成果都被人删除了你的反应会怎样还记得DROP
DATABASE 语句是没有记录的吗我们来学习一个流行的数据库管理系统Personal
Oracle 7 是如何实现安全的当你已经确认你的系统中没有选择Oracle 7时你也可能会将
这些信息中的大多数应用到其它的数据库管理系统中
技巧要带着下边的问题去规划你数据库系统的安全性
l 谁应该得到数据库管理员权限
l 有多少个用户需要访问数据库系统
l 每个用户应该得到什么样的权限与角色
l 当一个用户不再访问数据库时应该如何去删除它
Personal Oracle7 与安全
Personal Oracle7 通过下边的三个结构来实现安全性
l 用户
l 角色
l 权限
创建用户
用户就是允许登录进入系统的账号名创建用户使用的SQL 语法格式如下
语法
CREATE USER user
IDENTIFIED {BY password | EXTERNALLY}
[DEFAULT TABLESPACE tablespace]
[TEMPORARY TABLESPACE tablespace]
[QUOTA {integer [K|M] | UNLIMITED} ON tablespace]
[PROFILE profile]
如果你选择了需要密码选项当用户每次登录进行系统时系统都会要求他输入密码
作为一个例子下面请为你自己创建一个用户名
INPUT/OUTPUT
SQL> CREATE USER Bryan IDENTIFIED BY CUTIGER;
SQL 21 日自学通(V1.0) 翻译人笨猪
241
User created
每次我登录进行系统的名字是Bryan 我会被要求输入密码GUTIGER
如果选择了EXTERNALLY 选项ORACLE 将会依赖于你登录进入计算机系统的用户
名和密码也就是说当你登录进行计算机时你就已经登录进行了ORACLE
注一些解释器允许你使用外部的比如操作系统的密码作为SQL 的密码IDENTIFIED
externally 但是我们推荐你使用IDENTIFIED BY 子句强制用户在登录进行系统时
输入密码IDENTIFIED BY PASSWORD
就像你在CREATE USER 的其它部分所看到的一样ORACLE 允许你指定默认的表空
间及配额你可以在ORACLE 的附带文档中学习到更多的与之相关的内容
与你在本书中学到的其它的CREATE 语句一样它也有ALTER USER 命令其语法
格式如下
语法
ALTER USER user
[IDENTIFIED {BY password | EXTERNALLY}]
[DEFAULT TABLESPACE tablespace]
[TEMPORARY TABLESPACE tablespace]
[QUOTA {integer [K|M] | UNLIMITED} ON tablespace]
[PROFILE profile]
[DEFAULT ROLE { role [, role] ...
| ALL [EXCEPT role [, role] ...] | NONE}]
你可以使用该命令来改变所有的用户选项包括密码和配置文件例如如果想改变
Bryan 的密码你可以用下边的语句
INPUT/OUTPUT
SQL> ALTER USER Bryan
2 IDENTIFIED BY ROSEBUD
User altered
如果想改变默认的表空间可以用下边的语句
INPUT/OUTPUT
SQL> ALTER USER RON
SQL 21 日自学通(V1.0) 翻译人笨猪
242
2 DEFAULT TABLESPACE USERS
User altered.
如果想删除一个用户只需简单地使用DROP USER 命令即可它将会把用户从数据
库的清除该命令的语法格式如下
SYNTAX
DROP USER user_name [CASCADE]
如果你使用了CASCADE 选项那么所有与用户账号相关的对象也将会被删除否则
的话对象将仍归该用户所有但是用户将不再有效这有点让人迷糊但是当你只想删除
用户时它很有用
gototop
 

创建角色
角色是允许用户在数据库中执行特定功能的一个或一组权限将角色应用于用户的语
法如下
SYNTAX
GRANT role TO user [WITH ADMIN OPTION]
如果你使用了WITH ADMIN OPTION 选项那么该用户可以为其它用户赋予权限
功能是不是很强大
如果想删除角色可以使用REVOKE 命令
SYNTAX
REVOKE role FROM user
当你使用你早些时候创建的账号登录进入系统时你会为你的许可权问题而费尽精力
你可以登录进入但这也是你所能做的全部内容ORACLE 可以让你用下边的三个角色之
一进行注册
l Connect
l Resource
l DBA 也就是数据库管理员
这三个角色有着不同程度的权限
注如果你有适当的权限你可以创建你自己的角色为你的角色赋予权限然后将角色
应用于你的用户以取得更高的安全性
SQL 21 日自学通(V1.0) 翻译人笨猪
243
Connect 角色
你可以将Connect 角色理解为登录级角色被赋予该角色的用户可以登录进入系统并
做允许他/她们做的工作
INPUT/OUTPUT
SQL> GRANT CONNECT TO Bryan
Grant succeeded.
Connect 角色允许用户从表中插入更新删除属于其它用户的记录在取得了适当
的许可权限以后用户也可以创建表视图序列簇和同义词
Resource 角色
该角色允许用户对ORACLE 数据库进行更多的访问除了可以赋予Connect 角色的权
限以外它还有创建过程触发机制和索引的权限
INPUT/OUTPUT
SQL> GRANT RESOURCE TO Bryan
Grant succeeded.
DBA 角色
DBA 角色包括了所有的权限赋予了该角色的用户可以在数据库中做他们想做的任何
事为了保证系统的完整性你应该将具有该角色的用户数量保持在仅有的少数几个上
INPUT/OUTPUT
SQL> GRANT DBA TO Bryan;
Grant succeeded.
在经过了这三步之后用户Bryan 分别应用了connect resource 和DBA 角色这似乎
是多余的因为DBA 角色就包括了其它的两个角色所有现在我们可以把它删除掉
INPUT/OUTPUT
SQL> REVOKE CONNECT FROM Bryan
Revoke succeeded.
SQL> REVOKE RESOURCE FROM Bryan
Revoke succeeded.
SQL 21 日自学通(V1.0) 翻译人笨猪
244
拥有DBA 角色的Bryan 现在可以做他想做的任何工作
用户权限
在你决定好对你的用户应用何种角色以后你需要决定让你的用户具有使用哪种数据
库对象的权利ORACLE 中称之为许可权限该权限依据你为用户所定的角色的不同而不
同如果你创建了一个对象你可以将该对象的许可权赋予其它的用户以使他们也具有访
问权ORACLE 定义了两种可以赋予用户的权利— — 系统许可权与对象许可权见表12.1
和表12.2
系统许可权应用于整个系统赋予系统许可权的语法如下
SYNTAX
GRANT system_privilege TO {user_name | role | PUBLIC}
[WITH ADMIN OPTION]
如果使用了WITH ADMIN OPTION 选项就允许拥有该权限的人将该权限应用给其它
的用户
用户使用视图的权利
下边的命令将允许系统中的所有用户都具有在自己的模块中创建视图和访问视图的能

INPUT
SQL> GRANT CREATE VIEW TO PUBLIC
OUTPUT
Grant succeeded.
分析
PUBLIC 关键字的意思就是每个人都有创建视图的权利很明显系统权限允许受权
人访问几乎全部的系统的设置所有系统权限只应只能给予特定的人或需要使用系统权限
的人表12.1 显示出了你可以在ORACLE 7 的帮助文件中找到的系统权限
警告当赋予权限给PUBLIC 时必须小心它可能会给所有用户以访问数据库的权限尽
管有一些人你是不想让他拥有这一权限的
SQL 21 日自学通(V1.0) 翻译人笨猪
245
表12.1 在ORACLE 7 中的系统权限
System Privilege Operations Permitted
ALTER ANY INDEX 允许受权人更改任何模块中的索引
ALTER ANY PROCEDURE 允许受权人更改任何的存储过程函数并打包到任何模块中
ALTER ANY ROLE 允许受权人更改数据库中的任何角色
ALTER ANY TABLE 允许受权人更改模块中的表和视图
ALTER ANY TRIGGER 允许受权人能够不能或编译任何模块中的触发机制
ALTER DATABASE 允许受权人改变数据库
ALTER USER
允许受权人更改用户该权限允许受权人改变其它用户的密码或鉴定方
法指定配额或表空间设置默认的或临时的表空间指定一个profile
或默认角色
CREATE ANY INDEX 允许受权人在任何模块的任何表中创建索引
CREATE ANY PROCEDURE 允许受权人创建存储过程函数并打包到任何模块中
CREATE ANY TABLE
允许受权人在任何模块内创建表创建表的模块的所有者必须拥有配额
及表空间以存放表
CREATE ANY TRIGGER 允许受权人在与任何模块相关联的表的模块中创建触发机制
CREATE ANY VIEW 允许受权人在任何模块中创建视图
CREATE PROCEDURE 允许受权人创建存储过程函数并打包到他们自己的模块中
CREATE PROFILE 允许受权人创建profiles.
CREATE ROLE 允许受权人创建角色
CREATE SYNONYM 允许受权人在他们自己的模块中创建同义字
CREATE TABLE
允许受权人在他们自己的模块中创建表要创建一个表受权人必须有
表空间配额以存放表
CREATE TRIGGER 允许受权人在他们自己的模块中创建数据库触发机制
CREATE USER
允许受权人创建用户该权限也允许创建人指定表空间配额设置默认
和临时的表空间并指定profile作为CREATE USER语句的一部分
CREATE VIEW 允许受权人在他们自己的模块中创建视图
DELETE ANY TABLE 允许受权人从任何模块的表和视图中删除行或截表
DROP ANY INDEX 允许受权人从任何模块中删除索引
DROP ANY PROCEDURE 允许受权人删除任何模块中的包存储过程及函数
DROP ANY ROLE 允许受权人删除角色
DROP ANY SYNONYM 允许受权人删除任何模块中的同义字权限
DROP ANY TABLE 允许受权人删除任何模块中的表
DROP ANY TRIGGER 允许受权人删除任何模块中的数据库触发机制
DROP ANY VIEW 允许受权人删除任何模块中的视图
DROP USER 允许受权人删除用户
EXECUTE ANY PROCEDURE
允许受权人执行过程或函数独立的或打包的或引用任何模块中的全
局变量
GRANT ANY PRIVILEGE 使受权人具有全部系统特权
GRANT ANY ROLE 使受权人具有全部数据库角色
INSERT ANY TABLE 允许受权人在任何模块的表或视图中插入行
LOCK ANY TABLE 允许受权人锁定任何模块中的表和视定
SELECT ANY SEQUENCE 允许受权人引用其它模块中的序列
SELECT ANY TABLE 允许受权人查询任何模块中的表视图或进行映像
UPDATE ANY ROWS 允许受权人更新表中的行
对象权限是指可以在数据库中使用的对象的权限表12.2 中给出了所有了ORACLE
中的对象权限
SQL 21 日自学通(V1.0) 翻译人笨猪
246
你可以使用下边的GRANT 语句来对其它用户授权访问你的表
SYNTAX
GRANT {object_priv | ALL [PRIVILEGES]} [ (column
[, column]...) ]
[, {object_priv | ALL [PRIVILEGES]} [ (column
[, column] ...) ] ] ...
ON [schema.]object
TO {user | role | PUBLIC} [, {user | role | PUBLIC}] ...
[WITH GRANT OPTION]
如果你想取消对某个对象对于某人的授权你可以使用REVOKE 语句语法如下
SYNTAX
REVOKE {object_priv | ALL [PRIVILEGES]}
[, {object_priv | ALL [PRIVILEGES]} ]
ON [schema.]object
FROM {user | role | PUBLIC} [, {user | role | PUBLIC}]
[CASCADE CONSTRAINTS]
gototop
 

从建表到角色授权
创建一个名字为SALARIES 的表结构如下
INPUT
SQL> CREATE TABLE SALARIES (
2 NAME CHAR(30),
3 SALARY NUMBER,
4 AGE NUMBER);
OUTPUT
Table created.
现在来创建两个用户Jack 的Jill
INPUT/OUTPUT
SQL> create user Jack identified by Jack
ALL
ALTER
DELETE
EXECUTE
INDEX
INSERT
REFERENCES
SELECT
UPDATE
SQL 21 日自学通(V1.0) 翻译人笨猪
247
User created.
SQL> create user Jill identified by Jill
User created.
SQL> grant connect to Jack
Grant succeeded.
SQL> grant resource to Jill
Grant succeeded.
分析
到现在为止你已经创建了两个用户并且为每个用户分了不同的角色因此当他
们在数据库中进行工作时有着不同的能力在最初创建的表中有如下内容
INPUT/OUTPUT
SQL> SELECT * FROM SALARIES;
NAME SALARY AGE
JACK 35000 29
JILL 48000 42
JOHN 61000 55
在本例中你可以按着自己的意愿为该表分配不同的权限我们假定你具有DBA 角色
因而具有系统中的一切权利即使你不是DBA 角色你仍然可以对SALARIES 表进行对
象授权因为你是它的所有者
由于JACK 的角色了Connect 所以你只想让他有使用SELECT 语句的权利
INPUT/OUTPUT
SQL> GRANT SELECT ON SALARIES TO JACK
Grant succeeded.
因为JILL 的角色为Resource 你允许他对表进行选择和插入一些数据或是严格一些
允许JILL 修改SALARIES 表中SALARY 字段的值
INPUT/OUTPUT
SQL> GRANT SELECT, UPDATE(SALARY) ON SALARIES TO Jill
Grant succeeded.
现在表和用户都已经创建了你需要看一下创建的用户在访问表时的不同之处JACK
和JILL 都有对SALARIES 表执行SELECT 的权限可是如果是JACK 访问表他可能
会被告知该表不存在因为ORACLE 的表名之前需要知道表所有的的用户名或计划名
SQL 21 日自学通(V1.0) 翻译人笨猪
248
使用表时的限制
这里需要说明一下你在创建表的时候所使用的用户名假定为Byran 当JACK 想从
SALARIES 表中选择数据库他必须使用该用户名
INPUT
SQL> SELECT * FROM SALARIES
OUTPUT
ERROR at line 1:
ORA-00942: table or view does not exist
这里JACK 被告知该表并不存在现在对表使用用户名来加以标识
INPUT/OUTPUT
SQL> SELECT * FROM Bryan.SALARIES
NAME SALARY AGE
JACK 35000 29
JILL 48000 42
JOHN 61000 55
分析
你可以看到现在查询已经工作了现在我们再来测试一下JILL 的访问权限退出JACK
的登录并以JILL 的身份登录
INPUT/OUTPUT
SQL> SELECT * FROM Bryan.SALARIES
NAME SALARY AGE
JACK 35000 29
JILL 48000 42
JOHN 61000 55
工作正常现在试着向表中插入一个新的记录
INPUT/OUTPUT
SQL> INSERT INTO Bryan.SALARIES VALUES('JOE',85000,38)
ERROR at line 1:
ORA-01031: insufficient privileges
分析
SQL 21 日自学通(V1.0) 翻译人笨猪
249
该操作并没有被执行因为JILL 没有在SALARIES 表中使用INSERT 语句的权限
INPUT/OUTPUT
SQL> UPDATE Bryan.SALARIES SET AGE = 42 WHERE NAME = 'JOHN'
ERROR at line 1:
ORA-01031: insufficient privileges
分析
还是不能执行JILL 只能做他权利范围之内的事情事实上ORACLE 非常快地捕捉
到了错误并返馈给了她
INPUT/OUTPUT
SQL> UPDATE Bryan.SALARIES SET SALARY = 35000 WHERE NAME = 'JOHN'
1 row updated.
SQL> SELECT * FROM Bryan.SALARIES
NAME SALARY AGE
JACK 35000 29
JILL 48000 42
JOHN 35000 55
分析
你看到了JILL 可以进行她所有权限范围内的更新工作
为安全的目的而使用视图
我们在第十天的创建视图和索引中曾经提到过视图其实是一种虚表它可以为用
户提供一种以真实的数据并不相同的显示方式今天我们将学习更多的使用视图来实现
安全性的方法但是首先我们需要解释一下视图为什么可以让SQL 的语句简单化
在早些时候我们曾经学过当一个用户访问一个不为他所有的表的时候目标必须引用
它所属用户的名字方可正常访问就像你所想的一样当你将多个SQL 语句写在一行时它
将会变得非常的冗长更重要的是初学者在可以查看表的内容之前要先知道表的所属用
户名这并不是你想让你的用户做的工作下边给出了一个非常简单的解决方案
SQL 21 日自学通(V1.0) 翻译人笨猪
250
使用表或视图时限制的解决方法
假如你是以JACK 的身份登录进行系统的你从早些时候的内容中了解到如果你想查
看表中的内容你必须使用下边的语句
INPUT
SQL> SELECT * FROM Bryan.SALARIES
OUTPUT
NAME SALARY AGE
JACK 35000 29
JILL 48000 42
JOHN 35000 55
如果你创建了一个名字叫SELECT_VIEW 和视图那么用户可以非常简单地使用这个
视图
INPUT/OUTPUT
SQL> CREATE VIEW SALARY_VIEW AS SELECT * FROM Bryan.SALARIES
View created.
SQL> SELECT * FROM SALARY_VIEW
NAME SALARY AGE
JACK 35000 29
JILL 48000 42
JOHN 35000
分析
上边的查询返回的结果与使用用户名的返回结果是相同的
gototop
 

用同义词取代视图
SQL 还提供了一种叫同义词的对象同义词可以为表提供一个别名以将击键的次数减
到最小同义词有两种公有的和私有的任何一个具有Resource 角色的用户都可以创建
私有类型的同义词与之相对应的是只有DBA 角色的用户才能够创建公有类型的同义词
创建公有类型的同义词的语法如下
SYNTAX
CREATE [PUBLIC] SYNONYM [schema.]synonym FOR [schema.]object[@dblink]
SQL 21 日自学通(V1.0) 翻译人笨猪
251
针对前一个例子你可以使用下边的语句来取得相同的效果
INPUT/OUTPUT
SQL> CREATE PUBLIC SYNONYM SALARY FOR SALARIES
Synonym created.
然后再以JACK 的身份登录并输入
INPUT/OUTPUT
SQL> SELECT * FROM SALARY
NAME SALARY AGE
JACK 35000 29
JILL 48000 42
JOHN 35000 55
使用视图来解决安全问题
假定你现在又改变主意了你不想让JACK 和JILL 看到SALARIES 表的全部内容
你可以使用视图来达到只使他们看到属于他们自己的信息这一目的
INPUT/OUTPUT
SQL> CREATE VIEW JACK_SALARY AS
2 SELECT * FROM BRYAN.SALARIES
3 WHERE NAME = 'JACK';
View created.
INPUT/OUTPUT
SQL> CREATE VIEW JILL_SALARY AS
2 SELECT * FROM BRYAN.SALARIES
3 WHERE NAME = 'JILL';
View created.
INPUT/OUTPUT
SQL> GRANT SELECT ON JACK_SALARY TO JACK
Grant succeeded.
INPUT/OUTPUT
SQL> GRANT SELECT ON JILL_SALARY TO JILL
SQL 21 日自学通(V1.0) 翻译人笨猪
252
Grant succeeded.
INPUT/OUTPUT
SQL> REVOKE SELECT ON SALARIES FROM JACK
Revoke succeeded.
INPUT/OUTPUT
SQL> REVOKE SELECT ON SALARIES FROM JILL
Revoke succeeded.
现在以JACK 的身份登录并测试你为他创建的视图
INPUT/OUTPUT
SQL> SELECT * FROM Bryan.JACK_SALARY
NAME SALARY AGE
Jack 35000 29
INPUT/OUTPUT
SQL> SELECT * FROM PERKINS.SALARIES
ERROR at line 1:
ORA-00942: table or view does not exist
退出JACK 登录并以JILL 身份登录来测试JILL
INPUT/OUTPUT
SQL> SELECT * FROM Bryan.JILL_SALARY
NAME SALARY AGE
Jill 48000 42
ANALYSIS
你可以看到对SALARIES 表的访问将完全受到视图的控制SQL 允许你创建这些视图
并把它提供给你的用户这项技术为你提供了相当大的灵活性
删除同义词的语法如下
SYNTAX
SQL> drop [public] synonym synonym_name
注到现在为止你应该明白保持有DBA 角色的用户最少的重要性了吧因为具有这一
角色的用户可以运行数据库中的任何命令及操作但是请注意在ORACLE 和Sybase
中你只有成为DBA 角色的用户才可以从数据库中引入或导出数据
SQL 21 日自学通(V1.0) 翻译人笨猪
253
使用WITH GRANT OPTION 子句
如果JILL 想把她的UPDATE 权限赋给JACK 时需要谁来完成这项工作最初你可能
会认为应该由JILL 来完成因为她有UPDATE 权限她应该可以为其他用户受予这个权
限但是如果你使用早些时候的GRANT 语句JILL 并不能为其他用户授权
SQL> GRANT SELECT, UPDATE(SALARY) ON Bryan.SALARIES TO Jill
下边是我们在今天早些时候讲过的GRANT 语句的语法
SYNTAX
GRANT {object_priv | ALL [PRIVILEGES]} [ (column [, column]...) ]
[, {object_priv | ALL [PRIVILEGES]} [ (column[, column] ...) ] ] ...
ON [schema.]object TO {user | role | PUBLIC} [, {user | role | PUBLIC}] ...
[WITH GRANT OPTION]
看到在末尾的WITH GRANT OPTION 语句了吗当在给对象授权时如果使用了这个
选项那么该权限就可以被传给其他的用户所以如果你想让JILL 具有给JACK 授权的
能力那么你应该像下边这样使用GRANT 语句
INPUT
SQL> GRANT SELECT, UPDATE(SALARY)
2 ON Bryan.SALARIES TO JILL
3 WITH GRANT OPTION
OUTPUT
Grant succeeded.
当以JILL 的身份登录时就可以使用下边的语句
INPUT/OUTPUT
SQL> GRANT SELECT, UPDATE(SALARY) ON Bryan.SALARIES TO JACK
Grant succeeded.
总结
如果数据库的管理人员如果对数据库的安全考虑不周常常会导致许多问题幸运的是
SQL 提供了几个非常有用的命令来实现数据库的安全性
SQL 21 日自学通(V1.0) 翻译人笨猪
254
用户最初可由CREATE USER 命令来创建这里可以设定用户名和密码当用户的帐
号生效以后必须为其指定角色以使其可以工作在ORACLE 中有三种可用的角色分别
了Connect Resource 和DBA 角色每种角色有不同的访问数据库的资格Connect 最少
而DBA 则拥有全部的访问能力
GRANT 命名可以对用户进行授权REVOKE 命令则可以取消对用户的授权权限可
分为对象权限和系统权限系统权限应该严格控制不能授予没有使用经验的用户如果
他们得到了这种权限他们就可能也许是不经意之间毁坏你辛苦构建的数据库对象权
限则可以让用户具有访问个别的由已存在用户所创建的模块中的对象的能力
所有的这些技术和语句都为SQL 的用户提供了相当广泛的用以设置安全性的工具尽
管我们讨论的主要是ORACLE 7 的安全特性但是你可以在设计你自己的数据库时用到这
些信息切记不论你使用哪一种数据库产品它都提供了一定程度的安全性
问与答
问我知道安全性是需要的但是否ORACLE 做的太多了
答不一点也不多尤其是当在大型的多用户应用场合时更是这样由于使用数据库的
不同用户所做的工作也并不相同所以你需要限制用户让他们能做什么或不能做什么用
户应该只能进行他所处角色和权限许可内的工作
问看来DBA 用户在创建我的帐号时已经知道了我的密码是否是这样这是一个安全
问题
答确实是这样DBA 创建了你的用户和密码所以你应该在收到创建信息以后立即使用
ALTER USER 命令来更改ID 和密码
校练场
1 下边的语句是否是错误的
SQL> GRANT CONNECTION TO DAVID
2 对与错当删除用户时所有属于用户对对象都会随之删除
3 如果你创建了一个表并对它使用了SELECT 权限对象为PUBLC 时会有什么问题
4 下边的SQL 语句是否正确
SQL> create user RON identified by RON
SQL 21 日自学通(V1.0) 翻译人笨猪
255
5 下边的SQL 语句是否正确
SQL> alter RON identified by RON
6 下边的SQL 语句是否正确
SQL> grant connect, resource to RON
7 如果表为你所有别人如何才能从表中选择数据
练习
作为数据库安全性的练习请你创建一个表然后再创建一个用户为该用户设置不
同的安全性并测试
gototop
 

第13 天高级SQL
目标
在之前的12 天中我们学习了许多关于如何写出强在的SQL 查询以从数据库中获得
数据我们也简要的学习了如何进行数据库的设计以数据库安全性问题而在今天的高级
SQL 部分我们将主要学习以下内容
l 临时表
l 游标
l 存贮过程
l 触发机制
l 内嵌SQL
注在今天的例子中我们使用ORACLE 的PL/SQL 和MicroSoft SQL Server 的Transact-SQL
来实现我们尽可能使我们的例子可以适用于这两种风格的SQL 你不必为此需要得
到一个ORACLE 或SQL Server 的副本实际的数据库版本的选择是依据你的需要而定
的如果你已经阅读了足够的可以创建一个项目所需要的知识那你是没有选择的机
会的
注尽管你可以在大多数的数据库产品上应用本书中所给出的例子但是今天的内容
并不适用于这句话许多数据库供应商仍没有提供临时表存贮过程以及触发机制请检
查你的数据库文档看你所喜爱的数据库系统是否支持这些特性
临时表
我们要讨论的第一个高级主题是临时表的用法这是一种简单的临时存在于数据库系
统当中的表格当结束数据库的联接或退出登录以后它们会被自动地删除Transact-SQL 在
TempDB 中创建临时表这个数据库是在你安装SQL-SERVER 时创建的创建临时表可以
使用两种语法格式
SYNTAX
SYNTAX 1
SQL 21 日自学通(V1.0) 翻译人笨猪
257
create table #table_name (field1 datatype,
. . .
fieldn datatype
语法1 用以在TempDB 中创建一个表该表由Create Table 命令以及创建表时的日期
和时间组合而成一个唯一的表名临时表只可由它的创建者使用五十个用户可以在同时
运行下边的命令
1> create table #albums (
2> artist char(30),
3> album_name char(50),
4> media_type int)
5> go
表名开头的#标志是SQL 用以标识临时表的标志五十个用户中每一个都可以获得一
个他可以使用的表每一个用户都可以放心地插入更新删除表中的数据而不必担心其
它的用户使该表中的数据失效该表也可以使用下边的命令来手动删除
1> drop table #albums
2> go
当用户退出SQL-SERVER 时该表也可以被自动地删除如果你是在自态SQL 联接情
况下使用该语句例如SQL-SERVER 的DB-LIBRARY 那么当动态联接被终止时该表也
会被自动地删除
语法2 给出了另一种在SQL-SERVER 中创建临时表的方法该语法与使用语法1 有着
不同的结果所以你要注意这两种语法之间的差别
SYNTAX
SYNTAX 2:
create table tempdb..tablename (field1 datatype,
.
fieldn datatype)
使用语法2 来创建临时表的结果与使用语法1 相同临时表的名称格式也与语法1 的
相同它们的不同之处在于当用户退出SQL 或切断联接时该表不会被自动地删除用户必
SQL 21 日自学通(V1.0) 翻译人笨猪
258
须使用DROP 命令在TEMPDB中将其手动删除
技巧另外一种将使用语法2 创建的临时表删除的方法是将SQL-SERVER 关闭并重新启动
这将会把所有在TEMPDB 中的表都删除掉
例13.1 和13.2 表明使用这两种格式的临时表的确是货真价实的临时的表在这两个例
子以后例13.3 给出的临时的最为通常的用途用于暂时存贮从查询中返回的数据这些
数据可以在其它的查询中使用
为了验证这些例子你需要创建一个数据库在MUSIC 数据库中需要创建以下三个表
l ARTISTS
l MEDIA
l RECORDINGS
创建这些表需要使用下边的SQL 语句
INPUT
1> create table ARTISTS (
2> name char(30),
3> homebase char(40),
4> style char(20),
5> artist_id int)
6> go
1> create table MEDIA (
2> media_type int,
3> description char(30),
4> price float)
5> go
1> create table RECORDINGS (
2> artist_id int,
3> media_type int,
4> title char(50),
5> year int)
6> go
注表13.1 13.2 13.3 给出的这些表中的示例数据
表13.1
Name Homebase Style Artist_ID
Soul Asylum Minneapolis Rock 1
Maurice Ravel France Classical 2
Dave Matthews Band Charlottesville Rock 3
Vince Gill Nashville Country 4
Oingo Boingo Los Angeles Pop 5
Crowded House New Zealand Pop 6
Mary Chapin-Carpenter Nashville Country 7
SQL 21 日自学通(V1.0) 翻译人笨猪
259
Edward MacDowell U.S.A. Classical 8
表13.2
Media_Typ
e
Description
Price
1 Record 4.99
2 Tape 9.99
3 CD 13.99
4 CD-ROM 29.99
5 DAT 19.99
表13.3
Artist_Id Media_Type
Title Year
1 2 Hang Time 1988
1 3 Made to Be Broken 1986
2 3 Bolero 1990
3 5 Under the Table and Dreaming 1994
4 3 When Love Finds You 1994
5 2 Boingo 1987
5 1 Dead Man's Party 1984
6 2 Woodface 1990
6 3 Together Alone 1993
7 5 Come On, Come On 1992
7 3 Stones in the Road 1994
8 5 Second Piano Concerto 1985
例13.1
你可以在TEMPDB 数据库中创建一个临时表在向这些表中插入一些虚拟的数据以后
退出登录然后再重新登录SQL SERVER 试着从表中选取临时的数据注意结果
INPUT:
1> create table #albums (
2> artist char(30),
3> album_name char(50),
4> media_type int)
5> go
1> insert #albums values ("The Replacements", "Pleased To Meet Me", 1)
2> go
SQL 21 日自学通(V1.0) 翻译人笨猪
260
现在请使用EXIT 或者QUIT 来退出SQL SERVER 的联接在重新登录并选择了
你在上一次时使用的数据库以后试一下下边的命令
INPUT:
1> select * from #albums
2> go
分析
在当前的数据库中并不存在该表
例13.2
现在使用语法2 来创建表
INPUT:
1> create table tempdb..albums (
2> artist char(30),
3> album_name char(50),
4> media_type int)
5> go
1> insert #albums values ("The Replacements", "Pleased To Meet Me", 1)
2> go
在退出登录并重新登录进入以后切换到你在CREATE TABLE TEMPDB..ALBUMS
命令中指明的数据库然后请验证下边的命令
INPUT:
1> select * from #albums
2> go
这次你会得到下边的结果
OUTPUT:
Artist Album_name media_type
The Replacements Pleased To Meet Me 1
例13.3
本例给出一临时表的最为通常的用法在复合查询中存贮查询的结果为之后的查询使

INPUT
SQL 21 日自学通(V1.0) 翻译人笨猪
261
1> create table #temp_info (
2> name char(30),
3> homebase char(40),
4> style char(20),
5> artist_id int)
6> insert #temp_info
7> select * from ARTISTS where homebase = "Nashville"
8> select RECORDINGS.* from RECORDINGS, ARTISTS
9> where RECORDINGS.artist_id = #temp_info.artist_id
10> go
上边的这一组命令选出了所有的居住在Nashville 的艺术家的记录信息
下边的这此命令则是例13.3 语句的另外一种写法
1> select ARTISTS.* from ARTISTS, RECORDINGS where ARTISTS.homebase = "Nashville"
2> go
gototop
 

游标
数据库指针类似于字处理程序中的指针当你按下方向键时游标依次从各行文本中
滚动按一下向上键游标向上跳一行而按PageUp 和PageDown 则会向一次翻阅几行数
据库游标的操作也类似
数据库游标允许你选择一组数据通过翻阅这组数据记录通常被称为数据集检查
每一个游标所在的特定的行你可以将游标和局部变量组合在一起对每一个记录进行检查
当游标移动到下一个记录时来执行一些外部操作
游标的另一个常见的用法是保存查询结果以备以后使用一个游标结果集是通过执行
SELECT 查询来建立的如果你的应用程序或过程需要重复使用一组记录那么第一次建
立游标以后再重复使用将会比多次执行查询快得多而且你还有在查询的结果集中翻阅的
好处
下边是创建使用和关闭数据库游标的例子
1. Create the cursor.
2. Open the cursor for use within the procedure or application.
SQL 21 日自学通(V1.0) 翻译人笨猪
262
3. Fetch a record's data one row at a time until you have reached the end of the cursor's records.
4. Close the cursor when you are finished with it.
5. Deallocate the cursor to completely discard it.
创建游标
如果使用Transcat-SQL 来创建游标其语法如下
SYNTAX
declare cursor_name cursor
for select_statement
[for {read only | update [of column_name_list]}]
使用ORACLE7 的SQL 来创建和语法格式则如下
SYNTAX
DECLARE cursor_name CURSOR
FOR {SELECT command | statement_name | block_name}
在执行DECLARE cursor_name CURSOR 语句时你必须同时定义将要在你的所有的
游标操作中使用的结果集一个游标有两个重要的部分游标结果集和游标的位置
下边的语句将创建一个基于ARTIST 表的结果集
INPUT
1> create Artists_Cursor cursor
2> for select * from ARTISTS
3> go
分析
你现在已经有了一个名字为ARTIST_Cursor 游标它包括了所有的ARTIST 表的内容
但是首先你必须打开游标
打开游标
最简单的打开游标命令如下
SYNTAX:
open cursor_name
SQL 21 日自学通(V1.0) 翻译人笨猪
263
运行下列命令打开ARTIST_Cursor 游标
1> open Artists_Cursor
2> go
现在你可以使用游标来翻阅结果集了
使用游标来进行翻阅
要想在游标结果集中进行翻阅操作Transcat-SQL 提供了FETCH 命令
SYNTAX
fetch cursor_name [into fetch_target_list]
ORACLE SQL 则提供了下边的语法
FETCH cursor_name {INTO : host_variable
[[INDICATOR] : indicator_variable]
[, : host_variable
[[INDICATOR] : indicator_variable] ]...
| USING DESCRIPTOR descriptor }
每次当FETCH 命令运行时游标指针的好处是每次可以在结果集中移动一行如果
需要移动到行的数据可以被填充到fetch_target_list 变量中
注Transcat-SQL 允许程序员通过下边的命令来实现一次移动多行
set cursor rows number for cursor_name
该命令不能使用INTO 子句但是当向前跳动的行数已知时用它来代替重复执行
FETCH 命令则很有用
下边的语句将从ARTIST_Cursor 的结果集中获得数据并把它返回给程序变量
INPUT
1> declare @name char(30)
2> declare @homebase char(40)
3> declare @style char(20)
4> declare @artist_id int
5> fetch Artists_Cursor into @name, @homebase, @style, @artist_id
6> print @name
SQL 21 日自学通(V1.0) 翻译人笨猪
264
7> print @homebase
8> print @style
9> print char(@artist_id)
10> go
你可以使用WHILE 循环来循环查看整个结果集但是你是如果知道已经到达了最后
一个记录的呢
测试游标的状态
Transcat-SQL 可以让你在任何时候通常维护@@sqlstatus 和@@rowcount 这两个全
局变量来检查当前游标的状态
变量@@sqlstatus 返回最后一次运行FETCH 语句的状态信息Transcat-SQL 规定除
了FETCH 命令以外其他的命令不得修改@@sqlstatus 变量该变量可以取下表三个值中
的一个下表是在Transcat-SQL 参考手册中给出的
Status Meaning
0 Successful completion of the FETCH statement.
1 The FETCH statement resulted in an error.
2 There is no more data in the result set.
而变量@@rowcount 则返回上次一FETCH 命令设置的行号你可以用它来确定当前
游标结果集的行数
下边的代码给出了FETCH 命令的扩充使用方法你现在可以使用While Loop 命令和
变量@@sqlstatus 来翻阅当前的游标
INPUT
1> declare @name char(30)
2> declare @homebase char(40)
3> declare @style char(20)
4> declare @artist_id int
5> fetch Artists_Cursor into @name, @homebase, @style, @artist_id
6> while (@@sqlstatus = 0)
7> begin
8> print @name
SQL 21 日自学通(V1.0) 翻译人笨猪
265
9> print @homebase
10> print @style
11> print char(@artist_id)
12> fetch Artists_Cursor into @name, @homebase, @style, @artist_id
13> end
14> go
分析
现在你已经有了一个全功能的游标下边要做的工作就是关闭游标
关闭游标
关闭游标是一个非常简单的工作它的语句如下
SYNTAX
close cursor_name
这时游标依然存在但是它必须被再次打开方可使用关闭一个游标从本质上来说
是关闭了它的结果集而并不是它的全部内容如果你已经完全结束了对一个游标的使用
的话DEALLOCATE 命令将释放让游标所占用的内存并且可以让游标的名字可以被再次
使用这是该命令的语法格式
SYNTAX
deallocate cursor cursor_name
例13.4 给出了用Transcat-SQL 写的创建使用关闭释放一个游标的完整过程
Example 13.4
INPUT
1> declare @name char(30)
2> declare @homebase char(40)
3> declare @style char(20)
4> declare @artist_id int
5> create Artists_Cursor cursor
6> for select * from ARTISTS
7> open Artists_Cursor
SQL 21 日自学通(V1.0) 翻译人笨猪
266
8> fetch Artists_Cursor into @name, @homebase, @style, @artist_id
9> while (@@sqlstatus = 0)
10> begin
11> print @name
12> print @homebase
13> print @style
14> print char(@artist_id)
15> fetch Artists_Cursor into @name, @homebase, @style, @artist_id
16> end
17> close Artists_Cursor
18> deallocate cursor Artists_Cursor
19> go
注下边是示例所用的数据
OUTPUT:
Soul Asylum Minneapolis Rock 1
Maurice Ravel France Classical 2
Dave Matthews Band Charlottesville Rock 3
Vince Gill Nashville Country 4
Oingo Boingo Los Angeles Pop 5
Crowded House New Zealand Pop 6
Mary Chapin-Carpenter Nashville Country 7
Edward MacDowell U.S.A. Classical 8
gototop
 

游标的适用范围
与表索引以及其它的对象如触发机制和存贮过程不同游标在创建以后并不作为一
个数据库对象来看待所以游标的使用会受到一些限制
警告切记无论何时要注意游标分配过的内存尽管它的名字可能已经不存在了当不
在使用游标的时候或在进行游标能力之外的工作时一定要记得关闭游标并将它
释放掉
可以在下列三种情况下创建游标
l 在会话中— — 会话在用户登录以后开始如果用户在登录进行SQL SERVER 以后创建
了一个游标那么游标的名字将一直存在到用户退出登录用户不能再一次使用在本
SQL 21 日自学通(V1.0) 翻译人笨猪
267
次登录中创建的游标名
l 在存贮过程中— — 游标在存贮过程的内部创建的好处在于只有当过程运行时它才真正
起作用一旦过程退出了则游标的名字将不再有效
l 在触发机制中— — 在触发机制中创建游标与在存贮过程中创建游标所受到的限制是相
同的
创建和使用存贮过程
存贮过程是一个专业数据库编程人员必须掌握的概念存贮过程可以在最大程序上发
挥出SQL 的潜能该功能可以被如C FORTRAN 或VISUAL BASIC 象调用或执行自己
的函数一样地调用或执行存贮过程应该是一组经过压缩处理的经常使用的一组命令如
交叉表的查询更新和插入操作存贮过程允许程序员简单地将该过程作为一个函数来调
用而不是重复地执行过程内部的语句而且存贮过程还有些附加的优点
SyBase 工业有限公司是使用存贮过程的先驱它早在1980 年就在它的SQL SERVER
中提供了存贮过程功能这种过程是作为数据库的一部分被创建的它与表索引一样是
存贮在数据库的内部的Transcat-SQL 允许在过程调用中提供输入或输出的参数这种机
制可以让你写出通用的存贮过程并将变量传递给它
使用存贮过程的一个最大的优点在于它可以在设计的阶段执行当在一个网站中执行
大批量的SQL 语句时你的应用程序会不停地不SQL SERVER 进行通讯这会使得网站
的负荷迅速增大在多用户环境下通讯将异常繁忙你的服务器将变得越来越慢而使用
存贮过程可以在最大程序中减轻通讯负荷
当存贮过程执行时SQL 语句将在服务器中继续运行一些数据信息将会返回给用户
的电脑直至过程执行完毕这会极大地提高性能并带来了附加的好处存贮过程在第一次
执行时在数据库经过了编译操作编译的映象将存贮在服务器的过程中因此你不必在
每一次执行它的时候都对它进行优化这也使性能得到了提高
使用Transcat-SQL 来创建存贮过程的语法如下
SYNTAX
create procedure procedure_name
[[(]@parameter_name
datatype [(length) | (precision [, scale])
SQL 21 日自学通(V1.0) 翻译人笨猪
268
[= default][output]
[, @parameter_name
datatype [(length) | (precision [, scale])
[= default][output]]...[)]]
[with recompile]
as SQL_statements
运行存贮过程的EXECUTE 命令的语法如下
SYNTAX
execute [@return_status = ]
procedure_name
[[@parameter_name =] value |
[@parameter_name =] @variable [output]...]]
[with recompile]
例13.5
本例使用例13.4 的内容来创建一个简单的过程
INPUT
1> create procedure Print_Artists_Name
2> as
3> declare @name char(30)
4> declare @homebase char(40)
5> declare @style char(20)
6> declare @artist_id int
7> create Artists_Cursor cursor
8> for select * from ARTISTS
9> open Artists_Cursor
10> fetch Artists_Cursor into @name, @homebase, @style, @artist_id
11> while (@@sqlstatus = 0)
12> begin
13> print @name
14> fetch Artists_Cursor into @name, @homebase, @style, @artist_id
SQL 21 日自学通(V1.0) 翻译人笨猪
269
15> end
16> close Artists_Cursor
17> deallocate cursor Artists_Cursor
18> go
你可以使用EXECUTE 命令来执行Print_Artists_Name 过程
INPUT
1> execute Print_Artists_Name
2> go
OUTPUT
Soul Asylum
Maurice Ravel
Dave Matthews Band
Vince Gill
Oingo Boingo
Crowded House
Mary Chapin-Carpenter
Edward MacDowell
例13.5 是一个很小的存贮过程但是一个存贮过程中可以包含许多条语句也就是说你
不必逐条地执行这些语句
在存贮过程中使用参数
例13.5 是重要的第一步因为它给出的CREATE PRODUCE 语句的最简单的用法但
是在看过它的语法以后你会发现CREATE PRODUCE 语句有着比例13.5 更多的内容存
贮过程也可以接受参数并把它们输入到其中的SQL 语句中此外数据可以通过输出参数
从存贮过程中返回
输入参数必须以@提示符开始而且这些参数必须是Transcat-SQL 的合法数据类型
输出参数也必须以@提示符开始此外OUTPUT 关键字必须紧跟着输出参数的名字当
你在运行存贮过程时你必须给出OUTPUT 关键字
例13.6 给出了在存贮过程中使用输入参数的用法
SQL 21 日自学通(V1.0) 翻译人笨猪
270
例13.6
下面的存贮过程将选用所有发行媒体为CD 的艺术家的名字
1> create procedure Match_Names_To_Media @description char(30)
2> as
3> select ARTISTS.name from ARTISTS, MEDIA, RECORDINGS
4> where MEDIA.description = @description and
5> MEDIA.media_type = RECORDINGS.media_type and
6> RECORDINGS.artist_id = ARTISTS.artist_id
7> go
1> execute Match_Names_To_Media "CD"
2> go
运行该语句将会得到下边的结果集
OUTPUT
NAME
Soul Asylum
Maurice Ravel
Vince Gill
Crowded House
Mary Chapin-Carpenter
例13.7
本例中给出的输出参数的用法在该例中将使用艺术家的HOMEBASE 作为输入过
程会将艺术家的名字作为输出
INPUT
1> create procedure Match_Homebase_To_Name @homebase char(40), @name char(30) output
2> as
3> select @name = name from ARTISTS where homebase = @homebase
4> go
1> declare @return_name char(30)
2> execute Match_Homebase_To_Name "Los Angeles", @return_name = @name output
3> print @name
4> go
OUTPUT
Oingo Boingo
gototop
 

删除一个存贮过程
现在你大概会猜到应该如何删除一个存贮过程了如果你猜测是使用DROP 命令
那你是绝对正确的下边的语句将会从数据库中删除存贮过程
SYNTAX
drop procedure procedure_name
DROP 语句是经常使用的当一个存贮过程被重新创建之前旧的存贮过程以及它的
名字必须被删除掉根据我个人的经验只有极少的存贮过程在创建之后是不需要修改的
有许多次在语句中产生的错误会上传至过程我们推荐你在创建存贮过程时使用SQL 脚
本文件来存贮你在过程中的所有语句你可以在数据库服务器中使用这个脚本文件来得到
你想要的结果或者是对存贮过程进行重新编译该技术允许你使用通用的文本编辑器如VI
或WINDOWS 的记事本但是当你在运行脚本进行新的创建之前时你一定要记得将原
有的存贮过程及相关表先删除如果你忘记了执行DROP 命令你将会收到一个错误信息
在用SQL 的脚本来创建数据库对象时经常会用到下边的语法
SYNTAX
if exists (select * from sysobjects where name = "procedure_name")
begin
drop procedure procedure_name
end
go
create procedure procedure_name
as
该命令会检查SYSOBJECTS 表这里边存贮着SQL SERVER 的数据库对象信息来
查看该对象是否存在如果存在就在创建新对象之前先将它删除在创建脚本文件并最
后运行之前上面的工作将会花掉你大量的时间因为可能有太多的潜在错误出现
SQL 21 日自学通(V1.0) 翻译人笨猪
272
存贮过程的嵌套
存贮过程也可以被嵌套调用以增强程序的模块化一个存贮过程也可以被其它的存贮
过程调用它也可以调用其它的存贮过程嵌套的存贮过程在很多情况下是一个非常好的
办法
l 嵌套存贮过程可以在函数级上将你的复合查询减到最小如果在一行中需要运行
12 个查询你或许可以将这12 个查询精简为3 个嵌套过程调用当然这要视
情况而定
l 嵌套存贮过程可以提高性能查询优化将会更优化许多的简明语句组可能会比
大的语句组更有效
当嵌套存贮过程时所有的在存储过程内部创建的变量和数据库对象对于将要调用它
的过程来说都是可见的全体局部变量或临时对象如临时表将由最后创建这些元素的
存贮过程来删除
当准备一个大型的SQL 脚本文件时你可以要对表或数据库对象来运行它以检证问
题在调用它们之前你必须先创建一个嵌套的存贮过程来调用它但是主调过程可能会
创建临时表或游标以在调用过程中使用它们而被调用的过程则不知道这些将在脚本文件
的稍后时创建临时表和游标对于这个问题最容易的办法是在创建所有的存贮过程之前选
创建这些临时对象然后在存贮过程再次创建它们之前删除这些临时的对象在脚本文件
中你是不是对些有点迷糊了例13.8 将会帮助你明白这一过程
例13.8
INPUT
1> create procedure Example13_8b
2> as
3> select * from #temp_table
4> go
1> create procedure Example13_8a
2> as
3> create #temp_table (
4> data char(20),
5> numbers int)
SQL 21 日自学通(V1.0) 翻译人笨猪
273
6> execute Example13_8b
7> drop table #temp_table
8> go
分析
正如你所看到的过程Example13_8b 使用了但是#temp_table 并没有被创建它是
在过程Example13_8a 中创建的结果将会产生错误事实上Example13_8b 并没有被创
建因为并没有临时表#temp_table 而过程Example13_8a 也不会被创建因为没有过程
Example13_8b
下边的代码通过在创建第一个过程之前先创建#temp_table 来修正这一错误其实
#temp_table 在第二个过程创建之前被删除了
INPUT
1> create #temp_table (
2> data char(20),
3> numbers int)
4> go
1> create procedure Example13_8b
2> as
3> select * from #temp_table
4> go
1> drop table #temp_table
2> go
1> create procedure Example13_8a
2> as
3> create #temp_table (
4> data char(20),
5> numbers int)
6> execute Example13_8b
7> drop table #temp_table
8> go
SQL 21 日自学通(V1.0) 翻译人笨猪
274
设计和使用触发机制
触发机制从本质上来说是一种特殊类型的存贮过程它可以在下列的三种情况之一发生时
自动运行
l 更新
l 插入
l 删除
Transcat-SQL 创建触发机制的语法格式如下
SYNTAX
create trigger trigger_name
on table_name
for {insert, update, delete}
as SQL_Statements
ORACLE 7 SQL 则使用下边的语法来创建触发机制
SYNTAX
CREATE [OR REPLACE] TRIGGER [schema.]trigger_name
{BEFORE | AFTER}
{DELETE | INSERT | UPDATE [OF column[, column]...]}
[OR {DELETE | INSERT | UPDATE [OF column [, column] ...]}]...
ON [schema.]table
[[REFERENCING { OLD [AS] old [NEW [AS] new]
| NEW [AS] new [OLD [AS] old]}]
FOR EACH ROW
[WHEN (condition)] ]
pl/sql statements...
触发机制对于强制执行引用完整性非常有用我们在第9 天创建和维护表中学习
如何创建表时曾经提到过强制执行引用完整性可以保证在多表交叉访问时数据的有效性
如果用户输入了下边的命令
INPUT
1> insert RECORDINGS values (12, "The Cross of Changes", 3, 1994)
SQL 21 日自学通(V1.0) 翻译人笨猪
275
2> go
分析
这是一个有效的在表RECORDINGS 表中插入新记录的命令可是对ARTISTS 表进
行一下快速的检查后你会发现并没有ARTIST_ID=12 的记录用户所拥有的在
RECORDINGS 表中插入记录的权利可以彻底地破坏你的数据引用完整性
注尽管有许多数据库系统都可以通过在创建表的时候设置约束来强制执行数据的引
用完整性但是触发机制却提供了更为灵活的解决方法约束将会把系统的错误信息返回
给用户并且你现在可能已经知道了这些错误信息有时对你没有多大的帮助而作为
另外一种方法触发机制可以打印出错误信息调用其它的存贮过程如果有必要它还
可以修正错误信息
gototop
 

触发机制与事务处理
触发机制所进行的活动是被默认为事务处理的一部分进行的主要的事件次序如下
1 默认地自动运行BEGIN TRANSACTION 语句对于表和触发机制而言
2 当插入更新删除操作发生时
3 触发机制被调用其中的语句被自动执行
4 由触发机制自动的完成事务处理的取消或确认操作
例13.9
本例解决了早些时候更新RECORDINGS 表时所带来的问题
INPUT
1> create trigger check_artists
2> on RECORDINGS
3> for insert, update as
4> if not exists (select * from ARTISTS, RECORDINGS
5> where ARTISTS.artist_id = RECORDINGS.artist_id)
6> begin
7> print "Illegal Artist_ID!"
8> rollback transaction
9> end
SQL 21 日自学通(V1.0) 翻译人笨猪
276
10> go
分析
类似的问题也可能在删除RECORDINGS 表中的记录时出现如果你只是将
RECORDINGS 表中将某个艺术家删除了你可能也想同时删除ARTIST 表中的艺术家记
录如果在触发机制激活之前记录已经被删除了那么你如何才能知道哪一个ARTIST_ID
记录才是需要删除的记录呢对于这个问题有两种解决的办法
l 将ARTIST 表中的所有不在RECORDINGS 表中存在记录的艺术家删除见例
13.10a
l 检查被删除过的逻辑表Transcat-SQL 可以维护两个表DELETED 和
INSERTED 这两个表中保存着对真实表的最近所做的改动它与触发机制所创
建的表有着相同的结构因此你可以从DELETE 表中获得ARTIST_ID 的内容
并将它从ARTIST 中删除见例13.10b
例13.10a
INPUT
1> create trigger delete_artists
2> on RECORDINGS
3> for delete as
4> begin
5> delete from ARTISTS where artist_id not in
6> (select artist_id from RECORDINGS)
7> end
8> go
例13.10b
1> create trigger delete_artists
2> on RECORDINGS
3> for delete as
4> begin
5> delete ARTISTS from ARTISTS, deleted
6> where ARTIST.artist_id = deleted.artist_id
7> end
SQL 21 日自学通(V1.0) 翻译人笨猪
277
8> go
使用触发机制时的限制
l 当你在使用触发机制时你必须要知道它有如下的使用限制
l 不能在临时表中创建触发机制
l 触发机制必须在当前的表所在的数据库中创建
l 不能在视图中创建触发机制
l 当表被删除以后所有与之相关的触发机制会被自动地删除
触发机制的嵌套
触发机制也可以被嵌套比如说你可以创建一个触发机制来执行删除动作例如如
果触发机制自己删除了一个记录数据库服务器可以据此激活另一个触发机制结果将会
不停地循环直到表中的所有记录都被删除掉或一些其他的触发条件被激活嵌套机制
不是默认的可是环境中必须提供这个功能对于这个主题你可以参考你的数据库文档
来得到更多的内容
在选择语句中使用更新和删除
这是复合使用更新和删除语句的命令
INPUT
SQL> UPPDATE EMPLOYEE_TBL
SET LAST_NAME = 'SMITH'
WHERE EXISTS (SELECT EMPLOYEE_ID
FROM PAYROLL_TBL
WHERE EMPLOYEE_ID = 2);
OUTPUT
1 row updated.
分析
EMPLOYEE 表中有一个雇员的名字是不正确的我们只有当薪水表中出现的错误的ID
SQL 21 日自学通(V1.0) 翻译人笨猪
278
时才会更新雇员表
INPUT/OUTPUT
SQL> UPDATE EMPLOYEE_TABLE
SET HOURLY_PAY = 'HOURLY_PAY * 1.1
WHERE EMPLOYEE_ID = (SELECT EMPLOYEE_ID
FROM PAYROLL_TBL
WHERE EMPLOYEE_ID = '222222222');
1 row updated.
分析
我们将该雇员的小时报酬增加了10%
INPUT/OUTPUT
SQL> DELETE FROM EMPLOYEE_TBL
WHERE EMPLOYEE_ID = (SELECT EMPLOYEE_ID
FROM PAYROLL_TBL
WHERE EMPLOYEE_ID = '222222222';
1 row deleted.
分析
我们将雇员ID 号为222222222 的雇员删除了
在执行前测试选择语句
如果你正在创建报表比如你使用的是SQL*PLUS 而且报表是比较大的你也许会
想在运行之前先检查一个空格列标题这会浪费你许多的时间一个比较好的检查方
法是在你的SQL 语句中使用add where rownum < 3
SYNTAX
SQL> select *
from employee_tbl
where rownum < 5
分析
这时你可以得到表中的前四行用它你可以检查是否拼写空格和看起是否合适否
SQL 21 日自学通(V1.0) 翻译人笨猪
279
则在你发现报表中的拼写错误或不正确的空格时你的报表已经返回了成百上千行
技巧实现你的顾客的真正的需要是你的一项重要的工作它大概会占你所有工作中的一
半在特定的工作中拥有好的沟通手段和知识将会是你编程技能的有益补充例如
如果你为一个小汽车代理商工作它的经理想要知道在未来一段时间内他将会有多
少小汽车进帐你认为只是你自己对此进行计数将会很好他问的是他有多
少小汽车但是其实经理人真正想知道的是他有多少种类型的汽车小汽车卡
车汽车的型号生产的时间等等是不是他的要求会浪费你许多的时间或者说
你给他的是不是他所需要的
嵌入型SQL
本书中的嵌入型SQL 这一术语在用SQL 来编写大型程序时经常用到它的意思就是
可以将存贮过程嵌入到数据库之中并且它可以被应用程序来调用以处理一些任务一些数
据库系统提供了一整套的工具可以让你将SQL 与程序设计语言结合在一起用以创建简单的
屏幕和菜单对象SQL 代码被嵌入到这些代码之中
静态SQL 与动态SQL
静态SQL 的意思就是指在程序中直接写入SQL 代码这些代码在运行的时候不能被
更新事实上大多数静态SQL 解释器需要将你的SQL 语句在运行之前进行预编译处理
ORACLE 7 与INFORMIX 都为他们的数据库系统开发了静态SQL 包这些经过预编译的
产品可以在几种语言环境中使用主要有以下几种语言
l C
l PASCAL
l ADA
l COBLE
l FORTRAN
静态SQL 的好处在于
l 提高运行时的速度
l 经过了编译错误检查
SQL 21 日自学通(V1.0) 翻译人笨猪
280
它的缺点是
l 灵活性差
l 需要更多的代码因为查询不能在运行时进行变更
l 它对于其它的数据库系统来说使用起来不方便这一因素你必须要考虑到
如果你将这些代码打印出来的话SQL 的语言将会出现在C 语言的代码或者是你所
使用的那种语言在进行预编译处理时字段要受到程序变量的限制例13.11 是一个简单
的静态SQL 的使用实例
动态SQL 是另外一种方法它可以让程序员在运行时构建SQL 语句并把这些语句提
交给数据库引擎引擎再将数据返回给程序变量这也是在运行时实现的这一主题已经
在第12 天进行过非常彻底的讨论了
例13.11
本例给出了静态SQL 的C 语言中的使用方法注意到这里的语法并不是完全依照ANSI
标准来实施的静态SQL 不同于任何商业化的产品尽管它的语法与商业化的产品类似
INPUT:
BOOL Print_Employee_Info (void)
{
int Age = 0;
char Name[41] = "\0";
char Address[81] = "\0";
/* Now Bind Each Field We Will Select To a Program Variable */
#SQL BIND(AGE, Age)
#SQL BIND(NAME, Name);
#SQL BIND(ADDRESS, Address);
/* The above statements "bind" fields from the database to variables from the program.
After we query the database, we will scroll the records returned
and then print them to the screen */
#SQL SELECT AGE, NAME, ADDRESS FROM EMPLOYEES;
#SQL FIRST_RECORD
SQL 21 日自学通(V1.0) 翻译人笨猪
281
if (Age == NULL)
{
return FALSE;
}
while (Age != NULL)
{
printf("AGE = %d\n, Age);
printf("NAME = %s\n, Name);
printf("ADDRESS = %s\n", Address);
#SQL NEXT_RECORD
}
return TRUE;
}
分析
当你输入你的代码并把它们保存到文件之中以后这些代码通常要经过某种类型的预
编译处理方可运行预编译会将带有#SQL 的指令转换为真正在C 代码指令然后将它与
你其它的C 语言代码一同编译
如果你从来没有看过或写过C 语言的程序那么不要对例13.11 中的语法过份留心在
早些时候静态的SQL 只是一些伪指令请参阅在你的产品中的关于静态SQL 的真正语法
gototop
 

使用SQL 来编程
到目前为止我们已经讨论了两种在编程中使用的SQL 第一种方法来写查询和更新
数据已经在本书的第12 天中进行了详细的讨论第二种特点可以在第3 代或第4 代编程语
言中使用显然如果你想知道该语言和一般的数据库编程知识那么你可以使用第一种语
言我们已经讨论过了静态SQL 与动态SQL 相比的优点和缺点在第18 天PL/SQL 简
介和第19 天的Transcat-SQL 简介将会包括两种扩充的SQL 来取代嵌入型SQL 来完
成在这一部分讨论的相同类型的工作
SQL 21 日自学通(V1.0) 翻译人笨猪
282
总结
在通用的编程环境如VISUAL BASIC DELPHI 和POWERBUILDER 中为数据库编
程人员提供了许多的工具来对数据库进行查询和更新数据库的工作但是随着你对数据
库的日益深入你将会发现在今天讨论的主题中和使用这些工具的优点不幸的是有关
于游标触发机制和存贮过程等概念是在最近的数据库系统中提出的它们的标准化程序
还不高但是基本的使用的理论在几乎所有的数据库系统中都提供了
临时表是一种存在于用户会话过程中的表这种表是存在于一个特殊的数据库之中在
SQL SERVER 中称之为TEMPDB FETCH 语句用以从游标中获得指定的记录或使游标移
向下一个记录它经常将日期和时间作为其名称的唯一标识临时表可以存贮查询的结果
以供以后的查询使用如果多用户在同时在同时创建和使用了临时表由于在TEMPDB
中有大量的活动从而会导致系统性能的下降
存贮过程是一种可以将多个SQL 语句结合在一起作为一个函数的数据库对象存贮过
程可以接受和返回参数值并且可以被其它的存贮过程调用该过程是运行于数据库服务器
中并且是经过编译的使用存贮过程与直接使用SQL 语句相比可以提高系统的性能
内嵌型SQL 可以将SQL 代码用于实际编程中内嵌型SQL 可分为静态SQL 与动态SQL
两类静态SQL 不能是运行时进行修改而动态SQL 可以允许一定程序的修改
问与答
问如果我创建了一个临时表是否其它的用户可以使用我的表
答不能临时表只能由它的创建者使用
问为什么我必须关闭和释放游标
答内存仍为游标所占用既使它的名字不存在了也依然是这样
校练场
1 MICIRSOFT VISUAL C++可以让程序员直接调用ODBC 的API 函数对不对
2 ODBC 的API 函数只能由C 语言直接调用对不对
3 动态SQL 需要进行预编译对不对
4 临时表中的#提示符是干什么用的
SQL 21 日自学通(V1.0) 翻译人笨猪
283
5 在将游标从内存中关闭后必须做什么
6 能不能是SELECT 语句中使用触发机制
7 如果你在表中创建了触发机制然后你把表删除了那么触发机制还存在吗
练习
1 创建一个示例数据库应用程序在今天我们使用了音乐收藏数据库作为示例并对
应用程序进行合理的数据分组
2 列出你想要在数据库中完成的查询
3 列出你要在维护数据库中需要的各种规则
4 为你在第一步创建的数据库逻辑给创建不同的数据库计划
5 将第二步中的查询转变为存贮过程
6 将第三步中的规则转变为触发机制
7 将第4 步与第5 步结合起来与第6 步一起生成一个脚本其中包括所有的与该数
据库相关联的过程
8 插入一些示例数据这一步可以作为第7 步生成脚本的一部分
9 执行你所创建的这些过程并检证它的功能
gototop
 
12345   4  /  5  页   跳转
页面顶部
Powered by Discuz!NT