大型数据库技术项目
舰队管理系统分析与设计
舰队管理系统
1、项目背景介绍
中美海军实力对比分析:
中国海军虽然有了很大的进步,但是我们和世界先进的海军的实力有着很大差距,通过对比中美海军主战舰的数量和性能,中国海军要想拥有能与美国匹敌的能力和远距离投送兵力,至少需要十年的时间,或是更长的时间。
航空母舰
中国:无
美国:11艘核动力航母
两栖攻击舰
中国:无
美国:11艘可携带短距离起飞/垂直降落战斗机的两栖攻击舰
导弹巡洋舰
中国:无
美国:22艘导弹巡洋舰
驱逐舰:
中国:27艘吨位不同、性能参差不齐的驱逐舰
美国:60艘“阿利伯克”级驱逐舰
护卫舰:
中国:48艘护卫舰和护卫艇
美国:30艘护卫舰
潜艇
中国:3艘弹道核潜艇、6艘攻击型核潜艇、55艘柴电潜艇
美国:14艘弹道导弹核潜艇、57艘攻击型核潜艇、并以全部淘汰柴电潜艇
2、功能需求
美国海军的装备性能大幅领先于中国海军,近年来,美国又出台了新的军事战略政策,重返亚太,在这个关键时刻,依据我们现有的战舰,如何管理,分配人员,安装武器等是个首要问题。
在该系统中有四个模块:
1. 舰队信息管理
2. 舰艇信息管理
3. 兵官信息管理
4. 武器信息管理
同时在该四个系统中要分别进行插入,查询,修改等修改。
2.1 舰队管理系统完成主要功能
该系统的功能模块如下:
2.2系统功能的结构设计
系统功能的结构设计:
1. 舰队信息的基本输入,包括舰队名称、基地地点、舰艇数量
2. 舰队基本信息查询,修改与删除,包括舰队的名称、基地地点、舰艇数量
3. 舰艇信息的基本输入,包括舰艇编号、舰艇名称、武器名称、士兵数量、武器数量、舰艇功能
4. 舰艇基本信息查询、修改与删除,包括舰艇编号、舰艇名称、武器名称、士兵数量、武器数量、舰艇功能
5. 兵官信息的基本输入,包括官兵证号、舰艇编号、官兵姓名、工资、家乡地、年龄、性别
6. 兵官基本信息查询、修改、删除,包括官兵证号、舰艇编号、官兵姓名、工资、家乡地、年龄、性别
7. 武器基本信息的输入,包括武器型号、武器名称、武器生产时间、武器功能、武器名称、武器价格
8. 武器信息的查询、修改、删除,包括武器型号、武器名称、武器生产时间、武器功能、武器名称、武器价格
3、数据需求
根据调查分析,得出:
(1) 一个舰队有多艘舰艇;
(2) 一个舰队只能有一个舰队名称,一个舰队名称只能属于一个舰队;
(3) 一艘舰艇属于一支舰队;
(4) 一艘舰艇只能有一个舰艇编号,一个舰艇编号只能属于一艘舰艇;
(5) 一艘舰艇可以安装多种武器;
(6) 一种武器可安装于多艘舰艇上;
(7) 一种武器只能有一个武器名称,一个武器名称只能属于一种武器;
(8) 一艘舰艇有多个官兵;
(9) 一个官兵只属于一艘舰艇;
(10) 一个官兵只有一个证号,一个证号只属于一个官兵。
通过与用户的沟通,获得系统的数据存储需求如下:
舰队方面
舰队:舰队名称、基地地点、舰艇数量
舰艇:舰艇编号、舰艇名称、舰队名称、士兵数量、武器数量、舰艇功能
舰艇方面
舰艇:舰艇编号、舰艇名称、武器名称、士兵数量、武器数量、舰艇功能
武器:武器型号、武器名称、武器生产时间、武器功能、武器名称、武器价格
官兵:官兵证号、舰艇编号、官兵姓名、工资、家乡地、年龄、性别
3.1数据流图
3.2数据字典
3.2.1数据项:
数据项:舰队名称
含义说明:唯一标识每个舰队
别名:舰队名
类型:字符型
长度:12
数据项:基地地点
含义说明:标识基地地点
别名:基地名
类型:字符型
长度:12
数据项:舰艇数量
含义说明:舰艇的数量
别名:舰艇数
类型:数值型
长度:小数位数 0
取值范围:1~100
数据项:舰艇编号
含义说明:唯一标识每艘舰艇
别名:舰艇号
类型:字符型
长度:12
数据项:舰艇名称
含义说明:每艘舰艇的名称
别名:舰艇名
类型:字符型
长度:12
数据项:武器型号
含义说明:唯一标识每种武器的名称
别名:武器名
类型:字符型
长度:12
数据项:武器生产时间
含义说明:每种武器的出厂日期
别名:武器出产日
类型:日期型
数据项:官兵证号
含义说明:唯一标识每位官兵
别名:官兵号
类型:字符型
3.2.2数据结构:
数据结构:舰队
含义说明:舰队管理系统的主体数据结构,定义一个舰队有关信息
组成:舰队名称、基地地点、舰艇数量
数据结构:舰艇
含义说明:舰队管理系统的主体数据结构,定义一个舰艇的有关信息
组成:舰艇编号、舰艇名称、舰队名称、士兵数量、武器数量、舰艇功能
数据结构:武器
含义说明:舰队管理系统的主体数据结构,定义一种武器的有关信息
组成:武器型号、武器名称、武器生产时间、武器功能、武器名称、武器价格
数据结构:官兵
含义说明:舰队管理系统的主体数据结构,定义一个官兵的有关信息
组成:官兵证号、舰艇编号、官兵姓名、工资、家乡地、年龄、性别
3.2.3数据流:
数据流:安装
说明:舰艇上安装武器
数据流来源:武器
数据流去向:舰艇
组成:武器名称,舰艇编号,安装时间
平均流量:……
高峰期流量:……
数据流:拥有
说明:舰队拥有舰艇的数量
数据流来源:舰艇
数据流去向:舰队
组成:舰艇数量
平均流量:……
高峰期流量:……
数据流:属于
说明:舰艇上的官兵
数据流来源:官兵
数据流去向:舰艇
平均流量:……
高峰期流量:……
3.2.4数据存储:
数据存储:武器信息表
说明:记录武器的基本信息
流入数据流:……
流出数据流:舰艇
组成:武器型号、武器名称、武器生产时间、武器功能、武器名称、武器价格
数据量:……
存取方式:随机存取
数据存储:舰艇信息表
说明:记录舰艇的基本信息
流入数据流:……
流出数据流:舰队
组成:舰艇编号、舰艇名称、舰队名称、士兵数量、武器数量、舰艇功能
数据量:……
存取方式:随机存储
数据存储:官兵信息表
说明:记录官兵的基本信息
流入数据流:……
流出数据流:舰艇
组成:官兵证号、舰艇编号、官兵姓名、工资、家乡地、年龄、性别
数据量:……
存取方式:随机存取
数据存储:舰队信息表
说明:记录舰队基本信息
流入数据:舰艇
流出数据:……
组成:舰队名称、基地地点、舰艇数量
数据量:……
存取方式:随机存储
3.2.5处理过程:
处理过过程:武器对舰艇的安装
说明:为每艘舰艇安装武器
输入:舰艇编号、武器名称
输出:武器安装
处理:一种武器可安装于多艘舰艇上,一种武器只能有一个武器名称,一个武器名称只能属于一种武器。
处理过程:舰艇对官兵的分配
说明:为每艘舰艇分配官兵
输入:官兵证号、姓名、舰艇编号
输出:舰艇名
处理:一艘舰艇有多个官兵,一个官兵只能属于一艘舰艇。
处理过程:舰队对舰艇的分配
说明:为每支舰队分配舰艇
输入:舰艇编号、舰队名称
输出:舰队名称
处理:一支舰队有多艘舰艇,一艘舰艇只能属于一支舰队
4、概念结构设计阶段
将需求分析得到的用户需求抽象为抽象为反应用户观点的信息结构(即概念模型)的过程就是概念结构设计,它是整个数据库设计的关键。概念设计独立于计算机硬件结构,并且支持数据库的DBMS。
概念结构设计阶段主要是将需求分析阶段得到的用户需求抽象为信息(概念模型)的过程,它是整个数据库设计的关键。描述概念模型的方法有E-R图和UML图两种,这里采用比较常见的E-R模型表示方法。
具体设计步骤如下:
(1) 选择中层数据流为切入点,通常选择系统中的子系统
(2) 设计分E-R图,及各子模块的子系统
(3) 生成初步E-R图,通过合并的方法,统一各实体、属性、联系
(4) 生成全局E-R图,通过消除冲突等方面
Power Designer的使用
4.1 CDM
CDM:
4.2舰队实体
4.3武器实体
4.4兵官实体
4.5舰艇实体
4.6 整体E-R图
舰队:舰队名称、基地地点、舰艇数量
舰艇:舰艇编号、舰艇名称、武器名称、士兵数量、武器数量、舰艇功能
武器:武器型号、武器名称、武器生产时间、武器功能、武器名称、武器价格
官兵:官兵证号、舰艇编号、官兵姓名、工资、家乡地、年龄、性别
5、逻辑结构设计
概念结构是独立于任何一种数据模型的信息结构。逻辑结构设计的任务就是把概念结构的设计好的基本的E-R图转换为选用的DBMS产品所支持的逻辑结构。
5.1逻辑结构的设计步骤
概念模型映射为逻辑结构模型分为三步:
(1) 将概念模型转化为一般的关系、网状、层次模型。在这一步,映射不考虑特殊情况。
(2) 使用所选的DBMS来创建数据库的概念模式和外模式。不同的DBMS使用各自特定的特性和约束来实现数据模型。因此,有可能需对第一步所得的模型进行修改。该阶段得到的结果是用所选的DBMS语言编写的DDL语句。这些语句包括数据库系统的概念模式和外模式。
1)一个舰队有多艘舰艇;
2)一个舰队只能有一个舰队名称,一个舰队名称只能属于一个舰队;
3)一艘舰艇属于一支舰队;
4)一艘舰艇只能有一个舰艇编号,一个舰艇编号只能属于一艘舰艇;
5)一艘舰艇可以安装多种武器;
6)一种武器可安装于多艘舰艇上;
7)一种武器只能有一个武器名称,一个武器名称只能属于一种武器;
8)一艘舰艇有多个官兵;
9)一个官兵只属于一艘舰艇;
10)一个官兵只有一个证号,一个证号只属于一个官兵。
转换应遵循的原则:实体的属性就是关系的属性,实体的码就是关系的码。
5.2关系模式
分析得到的关系模式:
舰队(舰队名称,基地地点,舰艇数量)
舰艇(舰艇编号,舰队名称,舰艇名称,士兵数量,武器数量,舰艇功能)
官兵(官兵证号,舰艇编号,姓名,性别,年龄,家乡地,工资)
武器(武器型号,武器名称,武器生产时间,武器功能,价格)
安装(舰艇编号,武器名称,安装日期)
注:加下划线的是主键,加波浪线的是外键。
Fleet(Fleet_name,base, Warship_count)
Warship_ship(Warship_num,Fleet_name,Warship_name,Soldiers_count,Weapon_count,Warship_function)
Soldiers(Soldiers_num,Warship_num,Soldiers_name,Soldiers_sex,Soldiers_age, Soldiers_home, Soldiers_sal)
Weapon(Weapon_num,Weapon_name,Weapon_date,Weapon_function,Weapon_price)
Install(Warship_num,Weapon_name, Install_date)
5.3LDM:
(3)对数据模型进行优化
在舰队,舰艇,官兵,武器,安装,关系模式中,每个属性都已达到原子属性,则已达到1NF,同理在舰队,舰艇,官兵,武器,关系模式中因为候选键都完全函数决定非主属性,所以都达到了2NF;又因为在各个关系模式中候选键不传递决定于非主属性。则达到了3NF;
5.4数据库模式的定义
舰队基本信息表
列名 |
数据类型 |
可否为空 |
说明 |
FLEET_NAME |
CHAR(30) |
NOT NULL |
舰队名 |
WARSHIP_COUNT |
NUMBER(38) |
|
舰艇数量 |
BASE |
CHAR(40) |
NOY NULL |
基地 |
舰艇基本信息表
列名 |
数据类型 |
可否为空 |
说明 |
WARSHIP_NUM |
CHAR(30) |
NOT NULL |
舰艇编号 |
FLEET_NAME |
CHAR(30) |
|
舰队名 |
WARSHIP_NAME |
CHAR(30) |
NOT NULL |
舰艇名 |
SOLDIERS_COUNT |
NUMBER(38) |
|
士兵数量 |
WEAPON_COUNT |
NUMBER(38) |
|
武器数量 |
WARSHIP_FUNCTION |
CHAR(50) |
NOT NULL |
舰艇功能 |
士兵基本信息表
列名 |
数据类型 |
可否为空 |
说明 |
SOLDIER_NUM |
CHAR(30) |
NOT NULL |
士兵名 |
WARSHIP_NUM |
CHAR(30) |
|
舰艇编号 |
SOLDIERS_NAME |
CHAR(20) |
NOT NULL |
士兵名 |
SOLDIERS_SEX |
CHAR(2) |
|
士兵性别 |
SOLDIERS_AGE |
NUMBER(38) |
|
士兵年龄 |
SOLDIERS_HOME |
CHAR(50) |
|
家乡地 |
SOLDIERS_SAL |
NUMBER(38) |
|
工资 |
武器基本信息表
列名 |
数据类型 |
可否为空 |
说明 |
WEAPON_NUM |
CHAR(30) |
NOT NULL |
武器编号 |
WEAPON_NAME |
CHAR(50) |
NOT NULL |
武器名 |
WEAPON_DATE |
DATE |
|
生存日期 |
WEAPON_FUNCTION |
CHAR(50) |
NOT NULL |
武器功能 |
WEAPON_PRICE |
NUMBER(38) |
|
价格 |
安装信息表
列名 |
数据类型 |
可否为空 |
说明 |
WAESHIP_NUM |
CHAR(30) |
NOT NULL |
舰艇编号 |
WEAPON_NUM |
CHAR(30) |
NOT NULL |
武器编号 |
INSTALL_DATE |
DATE |
NOT NULL |
安装日期 |
六、数据库物理设计
数据库在物理设备上的存储结构与存取方法称为数据库的物理结构,它依赖于给定的计算机系统。
6.1数据库物理设计的步骤
(1) 确定数据库的物理结构,在关系数据库中的主要指存储方法和存储结构。
(2) 对物理就够进行评价,评价的重点是时间效率。
如果评价的结果满足原设计的要求,则可进入物理实施阶段,否则需重新设计或修改物理结构,有时甚至应返回逻辑设计阶段,修改数据模型。
关系数据库物理设计的内容:为关系模型选择存取方法(建立存取路径);设计关系,索引等数据库文件的物理存储结构。
6.2关系模式存取方式:
DBMS常用的存取方式有以下两种:
(1)索引存取
(2)聚簇存取
(3)HASH存取
现在主要就索引存取:
(1)在舰艇表的warship_name列上创建一个索引,这样就可以使在以舰艇名为条件查找其他信息时,提高查找的效率。
SQL> create index warship_name_index on warship_ship(warship_name);
索引已创建。
查找当舰艇名为两栖攻击舰时的舰艇编号和舰艇功能,就可以很快速的得到以下信息:
(2)创建复合索引:
下面这个例子就是通过查找士兵工资大于等于3000,且年龄小于30的士兵信息。首先要创建一个复合索引,在产品表的价格和产品总数两个列上创建复合索引。
6.3确定数据库的存储结构
主要是指确定数据的存放位置和存储结构,包括关系,索引,聚簇,日志,备份等的存储安排和存储结构:确定系统的配置等。确定数据的存放位置和存储结构要综合考虑存取时间,空间,维护代价三方面,这三方面常常是相互矛盾的,如消除一切冗余数据虽能够节约存储时间和减少维护代价,但往往会导致检索代价的增加,因此必须进行权衡,选择一个折中的方案。
(1) 确定数据的存放位置:根据应用情况将数据的异变部分与稳定部分、经常存取部分与存取频率较低部分分开存放,以提高系统性能。例如,数据库数据备份、日志文件备等由于只在故障恢复时才使用,而且数据量很大,可以考虑存放在磁带上:如果计算机有多个磁盘,可以考虑将表和索引部分分别存放在不同的磁盘上,查询时由于两个磁盘在工作,因而可以保证物理读写速度比较快:可以将较大的表分别放在两个磁盘上,以加快存取速度,在多用户环境下特别有效:可以将日志文件与数据库对象(表和索引)放在不同的磁盘上以改进系统性能。
(2) 确定系统配置:DBMS产品提供的存储分配参数有同时使用数据库的用户数和对象数、使用的缓冲区长度和个数、时间片大小、数据库大小、装填因子、锁的数目等。系统都为这些变量赋了合理的缺省值,但这些值不一定适应各种应用环境,物理设计需要根据应用环境确定这些参数值,以使系统性能最优。物理设计时对系统配置变量的调整只是初步的,在系统运行时还要根据系统实际运行情况作进一步的调整,以此来改进系统的性能。
6.4 PDM
7、数据库的实施及维护
完成了逻辑设计和物理设计后,就可以着手实现数据库系统了。通常,这部分工作有DBA负责,数据库设计人员协助共同完成。用DDL语言对概念模式和外模式,然后向数据库中装载数据。如果要从以前的系统中转换数据,可以使用转换程序重新对数据进行格式化,以便装载到新的数据库中。实施完成,系统进入正常的运行维护直至死亡。
7.1数据库的实施
1. 数据的载入
2. 应用程序的编制与调试
7.2数据库试运行
(1) 试运行的任务
(2) 数据的分期入库
(3) 数据库的转储和恢复
7.3数据的运行和维护
(1) 数据库的转储和恢复
(2) 数据库的安全性、完整性控制
(3) 数据库的监督、分析和改进
(4) 数据库的重组织和重构造
7.4创建表空间:
SQL> create tablespace wqh_tablespace
2 datafile 'f:\wqh_tablespace01.dbf' size 100m
3 extent management local
4 segment space management auto;
表空间已创建。
为了防止在使用过程中表空间不足,则可以为它添加新的数据文件以增大表空间(因为数据文件的大小决定了创建的表空间的大小,表空间的大小等于不同磁盘上所有数据文件的大小之和):
SQL> alter tablespace wqh_tablespace
2 add datafile 'd:\wqh_tablespace02.dbf' size 100m;
表空间已更改。
查看该表空间数据文件的路径:
7.5创建用户
SQL> create user wqh
2 identified by 1215115130
3 default tablespace wqh_tablespace
4 temporary tablespace temp;
用户已创建。
7.6分配权限:
SQL> grant create session,resource to wqh;
授权成功。
再给用户wqh授予创建视图的权限和为用户wqh授予创建同义词的权限:
SQL> conn / as sysdba;
已连接。
SQL> grant create view to wqh;
授权成功。
SQL> grant create synonym to wqh;
授权成功。
7.7创建用户配置文件:
创建一个名为res_profile的概要文件,要求每个用户最多可以创建5个并发会话;每个会话持续时间最长为60分钟;如果会话在连续30分钟内空闲,则结束会话;每个会话的私有SQL区为100 KB;每个SQL语句占用CPU时间总量不超过100秒。
SQL> create profile res_profile limit
2 sessions_per_user 5 connect_time 60
3 idle_time 30 private_sga 100k
4 cpu_per_call 100;
配置文件已创建
修改用户为用户指定概要文件:
SQL> alter user wqh profile res_profile;
用户已更改。
7.8从power designer的PDM阶段所得到的建表语句
如下:
舰队表:
create table Fleet
(
Fleet_name char(30) not null,
Warship_count integer null,
base char(40) not null,
constraint PK_FLEET primary key (Fleet_name)
);
舰艇表:
create table Warship_ship
(
Warship_num char(30) not null,
Fleet_name char(30) null,
Warship_name char(30) not null,
Soldiers_count numeric null,
Weapon_count numeric null,
Warship_function char(50) not null,
constraint PK_WARSHIP_SHIP primary key (Warship_num)
);
为舰艇表添加Fleet_name外键
alter table Warship_ship
add constraint FK_WARSHIP__HAVE_FLEET foreign key (Fleet_name)
references Fleet (Fleet_name);
士兵表:
create table Soldiers
(
Soldier_num char(30) not null,
Warship_num char(30) null,
Soldiers_name char(20) not null,
Soldiers_sex char(2) null,
Soldiers_age integer null,
Soldiers_home char(50) null,
Soldiers_sal numeric null,
constraint PK_SOLDIERS primary key (Soldier_num)
);
为士兵表添加Warship_num外键
alter table Soldiers
add constraint FK_SOLDIERS_BELONG_WARSHIP_ foreign key (Warship_num)
references Warship_ship (Warship_num);
武器表:
create table Weapon
(
Weapon_num char(30) not null,
Weapon_name char(50) not null,
Weapon_date date null,
Weapon_function char(50) not null,
Weapon_price numeric null,
constraint PK_WEAPON primary key (Weapon_num)
);
安装表:
create table Install
(
Warship_num char(30) not null,
Weapon_num char(30) not null,
Install_date date not null,
constraint PK_INSTALL primary key (Warship_num, Weapon_num)
);
为安装表添加Weapon_num外键
alter table Install
add constraint FK_INSTALL_IW_WEAPON foreign key (Weapon_num)
references Weapon (Weapon_num);
为安装表添加Warship_num外键
alter table Install
add constraint FK_INSTALL_WI_WARSHIP_ foreign key (Warship_num)
references Warship_ship (Warship_num);
7.9在oracle数据库的wqh用户进行建表
如下:
创建舰队表
创建舰艇表:
为舰艇表添加Fleet_name外键:
创建士兵表:
为士兵表添加Warship_num外键:
创建武器表:
创建安装表:
为安装表添加Weapon_num外键:
为安装表添加Warship_num外键:
7.10视图的创建
1. 创建一个视图,规定该视图中有士兵表中的士兵编号,舰艇编号,官兵姓名
2. 创建一个包含舰艇编号,舰队名称,士兵数量,武器数量的视图。
3. 可以创建一个连接视图,用来查询同一个舰队所包含的舰艇信息,以及员工名字的视图:
create view fleet_warship_view(舰艇编号,舰队名字,舰艇名字,基地地点)
as
select warship_ship.warship_num, warship_ship.fleet_name, warship_ship.warship_name,fleet.base
From warship_ship,fleet
Where warship_ship.fleet_name=fleet.fleet_name;
查询结果如下:
4.创建一个视图,里面有信息:员工编号,员工名,订单编号,订单日期,客户编号,客户名
create view warship_install_weapon_view(舰艇编号,舰队名称,武器数量,安装日期,武器型号,武器价格)
as
select warship_ship.warship_num, warship_ship.fleet_name, warship_ship.weapon_count,install.install_date,weapon.weapon_num,weapon.weapon_price
from warship_ship, install, weapon
where warship_ship.warship_num=install. warship_num and install.weapon_num=weapon.weapon_num;
视图已创建。
7.11创建同义词
同义词是数据库中表、索引、视图或其他模式对象的一个别名。若是为士兵表创建一个同义词,如果想为其中的某个士兵涨工资,则就可以通过该表的同义词来实现对士兵表的操作,如下:
这时可以通过更新同义词来实现对订单表的更新,如下:
update lists_synonym
set clerkno=’0001’
where lno=’A_000010’;
在没有更新之前的数据是:
现在更新之后,结果如下:
7.12存储过程的创建
1.创建一个存储过程,以舰艇号为参数,查询该舰艇中所有士兵的平均工资,并输出该舰艇中比平均工资高的士兵编号、士兵名。
CREATE OR REPLACE PROCEDURE soldiers_produce(
p_warship_num soldiers.warship_num%TYPE)
--以舰艇号为参数
AS
v_sal soldiers.soldiers_sal%TYPE;
BEGIN
SELECT avg(soldiers_sal) INTO v_sal FROM soldiers
WHERE warship_num=p_warship_num;
--查询该舰艇中所有士兵的平均工资
DBMS_OUTPUT.PUT_LINE(p_warship_num||' '||'average salary is:'
||v_sal);
FOR v_soldiers IN (SELECT * FROM soldiers
WHERE warship_num=p_warship_num AND soldiers_sal>v_sal) LOOP
DBMS_OUTPUT.PUT_LINE(v_soldiers.soldier_num||' '||v_soldiers.soldiers_name);
--输出该舰艇中比平均工资高的士兵编号、士兵名
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('The warship doesn’’t exists!');
--当舰艇不存在时,输出语句The warship doesn’’t exists!
END soldiers_produce;
执行的结果如下:
3. 创建一个存储过程,以舰艇号为参数,返回该舰艇所含有的士兵人数和士兵的平均工资。
CREATE OR REPLACE PROCEDURE warship_soldiers_pro(
p_warship_num soldiers.warship_num%TYPE,
p_avgsal OUT soldiers.soldiers_sal%TYPE,
p_count OUT soldiers.soldiers_sal%TYPE)
--以舰艇号为参数
AS
BEGIN
SELECT avg(soldiers_sal),count(*) INTO p_avgsal,p_count
--返回该舰艇士兵的平均工资和所含有的士兵人数
FROM soldiers
WHERE warship_num=p_warship_num;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('The warship doesn’’t exists!');
END warship_soldiers_pro;
3.输出某个舰艇士兵的姓名及工资档次,3000以上高,2500-3000中等,2000-2500较低,2000以下低。
create or replace procedure soldiers_name_level
(dnum in soldiers.soldier_num%type,
name out soldiers.soldiers_name%type,level out varchar2)
as
salary soldiers.soldiers_sal%type;
begin
select soldiers_sal,soldiers_name
into salary,name from soldiers
where dnum=soldier_num;
--查找士兵的工资和姓名
case
when salary >3000 then level:='高';
--当士兵的工资大于3000时输出:高
when salary between 2500 and 3000 then level:='中等';
--当士兵的工资大于2500小于3000时输出:中等
when salary between 2000 and 2500 then level:='较低';
--当士兵的工资大于2000小于2500时输出:较低
else level:='低';
--当士兵的工资低于2000时输出:较低
end case;
end;
/
4. 获取某舰艇所包含士兵的工资总和及该舰艇的名称。
create or replace procedure warship_pro
(e_warship_num soldiers.warship_num%type)
as
cursor soldiers_cur is--游标名为soldiers_cur
select warship_num,sum(soldiers_sal) as e_sal
from soldiers
where warship_num=e_warship_num
group by warship_num;
--以舰艇编号分组
begin
for soldiers_record1 in soldiers_cur loop
dbms_output.put_line(soldiers_record1.warship_num||' '
||soldiers_record1.e_sal);
--输出该舰艇所包含士兵的工资总和及该舰艇的名称
end loop;
end;
/
过程已创建。
7.13函数的创建
1. 创建一个以舰艇号为参数,返回该舰艇所包含的士兵中最高工资的函数。
CREATE OR REPLACE FUNCTION return_maxsal
(p_warship_num soldiers.warship_num%TYPE)
--以舰艇号为参数
RETURN soldiers.soldiers_sal%TYPE
--以士兵的工资作为返回值
AS
v_maxsal soldiers.soldiers_sal%TYPE;
BEGIN
SELECT max(soldiers_sal) INTO v_maxsal FROM soldiers
WHERE warship_num=p_warship_num;
RETURN v_maxsal;--返回该舰艇所包含的士兵中最高工资
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('The warship_num is invalid!');
END return_maxsal;
注释:在当舰艇号为056时所包含的员工中工资最高位3800
注释:在当舰艇号为0981时所包含的员工中工资最高位2800
2. 获取某士兵的工资:
create or replace function soldiers_sal
(soldier1_num in soldiers.soldier_num%type)
--以士兵的编号为参数
return soldiers.soldiers_sal%type
--返回士兵的工资
as
salary soldiers.soldiers_sal%type;
begin
select soldiers_sal into salary
from soldiers
where soldier1_num=soldier_num;
return salary;
--返回士兵的工资
end;
/
函数已创建。
3.计算指定舰艇中员工的工资总和,并统计该舰艇中的士兵数量。
create or replace function salnum_soldiernum
(dnum in soldiers.soldier_num%type,total out soldiers.soldiers_sal%type)
return number
--返回士兵的数量
as
e_count number;
begin
select sum(soldiers_sal),count(soldier_num)
--计算指定舰艇中员工的工资总和,统计该舰艇中的士兵数量
into total,e_count
from soldiers
where warship_num=dnum;
return e_count;
end;
/
函数已创建。
declare
total soldiers.soldiers_sal%type;
begin
dbms_output.put_line('snum:'||salnum_soldiernum('8',total)
||''||'total:'||total);
end;
/
4. 如果需要函数返回多个值,可以使用OUT或IN OUT模式参数。
创建一个函数,以舰艇号为参数,返回舰艇名、舰艇所包含的士兵人数及该舰艇的平均工资。
CREATE OR REPLACE function return_warship_soldiers(
p_warship_num warship_ship.warship_num%TYPE,
p_num OUT NUMBER,
p_avg OUT NUMBER) --以舰艇号为参数
RETURN warship_ship.warship_name%TYPE --返回舰艇名
AS
v_warship_name warship_ship.warship_name%TYPE;
BEGIN
SELECT warship_name INTO v_warship_name
FROM warship_ship
WHERE warship_num=p_warship_num;
SELECT count(*),avg(soldiers_sal)
INTO p_num,p_avg
FROM soldiers
WHERE warship_num=p_warship_num;
RETURN v_warship_name; --返回舰艇名
END return_warship_soldiers;
5. 输出某个士兵的姓名及工资档次,3000以上高,2500-3000中等,
2000-2500较低,2000以下低。
create or replace function soldiers_sal_level
(dnum in soldiers.soldier_num%type,
name out soldiers.soldiers_name%type)
--以士兵的士兵编号为参数
return varchar2
as
salary soldiers.soldiers_sal%type;
level varchar2(15);
begin
select soldiers_sal,soldiers_name into salary,name
from soldiers
where dnum=soldier_num;
case
when salary >3000 then level:='高';
--当士兵的工资大于3000时输出:高
when salary between 2500 and 3000 then level:='中等';
--当士兵的工资大于2500小于3000时输出:中等
when salary between 2000 and 2500 then level:='较低';
--当士兵的工资大于2000小于2500时输出:较低
else level:='低';
--当士兵的工资低于2000时输出:较低
end case;
return level;
end;
/
函数已创建。
7.14局部子程序:
在一个块内部定义一个函数和一个过程。
函数以舰艇号为参数返回该舰艇中士兵的平均工资;过程以舰艇号为参数,
输出该舰艇中士兵工资低于该舰艇平均工资的士兵的士兵号、士兵名。
DECLARE
v_warship_num soldiers.warship_num%TYPE;
v_avgsal soldiers.soldiers_sal%TYPE;
FUNCTION return_avgsal(p_warship_num soldiers.warship_num%TYPE)
--以舰艇编号为参数
RETURN soldiers.soldiers_sal%TYPE
--返回该舰艇中士兵的平均工资
AS
v_sal soldiers.soldiers_sal%TYPE;
BEGIN
SELECT avg(soldiers_sal) INTO v_sal FROM soldiers
WHERE warship_num=p_warship_num;
RETURN v_sal;
END return_avgsal;
PROCEDURE show_soldiers(p_warship_num soldiers.warship_num%TYPE)
--以舰艇号为参数
AS
CURSOR c_soldiers IS
SELECT * FROM soldiers WHERE warship_num=p_warship_num;
BEGIN
FOR v_soldiers IN c_soldiers LOOP
IF v_soldiers.soldiers_sal<return_avgsal(v_soldiers.warship_num)
THEN
DBMS_OUTPUT.PUT_LINE(v_soldiers.soldier_num||' '||
v_soldiers.soldiers_name);
--输出该舰艇中士兵工资低于该舰艇平均工资的士兵的士兵号、士兵名
END IF;
END LOOP;
END show_soldiers;
BEGIN
v_warship_num:=&x;
v_avgsal:=return_avgsal(v_warship_num);
show_soldiers(v_warship_num);
END;
7.15触发器的创建
1. 为soldiers士兵表创建一个触发器,当执行插入操作时,统计操作后士兵人数;当执行更新工资操作时,统计更新后士兵的平均工资;当执行删除操作时,统计删除后各个舰艇所包含士兵的人数。
CREATE OR REPLACE TRIGGER trg_soldiers_dml
AFTER INSERT OR UPDATE OR DELETE ON soldiers
DECLARE
v_count NUMBER;
v_sal NUMBER(6,2);
BEGIN
--当执行插入操作时,统计操作后士兵人数
IF INSERTING THEN
SELECT count(*) INTO v_count FROM soldiers;
DBMS_OUTPUT.PUT_LINE(v_count);
--当执行更新工资操作时,统计更新后士兵的平均工资;
ELSIF UPDATING THEN
SELECT avg(soldiers_sal) INTO v_sal FROM soldiers;
DBMS_OUTPUT.PUT_LINE(v_sal);
--当执行删除操作时,统计删除后各个舰艇所包含士兵的人数。
ELSE
FOR v_warship_ship IN
(SELECT warship_num,count(*) num FROM soldiers
GROUP BY warship_num) LOOP
DBMS_OUTPUT.PUT_LINE(v_warship_ship.warship_num
||' '||v_warship_ship.num);
END LOOP;
END IF;
END trg_soldiers_dml;
2. 修改士兵的工资时,保证修改后的士兵工资高于修改前的士兵工资。
CREATE OR REPLACE TRIGGER trg_soldiers_update_row
BEFORE UPDATE OF soldiers_sal ON soldiers
--指定触发时机为更新士兵soldiers表的列soldiers_sal操作前触发
FOR EACH ROW --说明创建的是行级触发器
WHEN(new.soldiers_sal<=old.soldiers_sal)
--当更新后的数小于更新前的数,则会输出-20001,'The soldiers_sal is lower!
BEGIN
RAISE_APPLICATION_ERROR(-20001,'The lnumber is lower!');
END trg_soldiers_update_row;
3.修改warship_ship表的warship_num列时,同时把soldiers表中相应的warship_num也作相应的修改;
set serveroutput on
CREATE OR REPLACE TRIGGER tr_warship_soldiers
AFTER update OF warship_num ON warship_ship
FOR EACH ROW --说明创建的是行级触发器
BEGIN
DBMS_OUTPUT.PUT_LINE('旧的warship_num值是'||:old.warship_num
||'、新的warship_num值是'||:new.warship_num);
UPDATE soldiers SET warship_num = :new.warship_num
WHERE :old.warship_num = warship_num;
END;
/
触发器已创建
3. 创建一个触发器,限制对订单表lists的修改,
(包括insert,delete,update)的时间范围,即禁止在星期天改变订单信息
CREATE OR REPLACE TRIGGER tr_soldiers_time
BEFORE INSERT OR DELETE OR UPDATE ON soldiers
BEGIN
IF (TO_CHAR(sysdate,'DAY') IN ('星期六', '星期日'))
OR (TO_CHAR(sysdate, 'HH24:MI') NOT BETWEEN '09:00' AND '18:30')
THEN
RAISE_APPLICATION_ERROR(-20000, '不是上班时间,不能修改soldiers表');
END IF;
END;
4. 为soldiers表创建一个触发器,当插入新士兵时显示新士兵的士兵号、士兵名;当更新士兵工资时,显示修改前后士兵工资;当删除士兵时,显示被删除的士兵号、士兵名。
CREATE OR REPLACE TRIGGER trg_soldiers_dml_row
BEFORE INSERT OR UPDATE OR DELETE ON soldiers
FOR EACH ROW
BEGIN
IF INSERTING THEN
--当插入新士兵时显示新士兵的士兵号、士兵名;
DBMS_OUTPUT.PUT_LINE(:new.soldier_num||' '||
:new.soldiers_name);
ELSIF UPDATING THEN
--当更新士兵工资时,显示修改前后士兵工资;
DBMS_OUTPUT.PUT_LINE(:old.soldiers_sal||' '||:new.soldiers_sal);
ELSE
--当删除士兵时,显示被删除的士兵号、士兵名。
DBMS_OUTPUT.PUT_LINE(:old.soldier_num||' '||:old.soldiers_name);
END IF;
END trg_soldiers_dml_row;
.