ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Oracle课程设计:图书管理系统建表、存储过程与触发器全解析

Oracle课程设计:图书管理系统建表、存储过程与触发器全解析 简介这份 Oracle 数据库课程设计报告以图书管理系统为例完整呈现了基于 Oracle 11g 后台数据库与 Visual Studio 2005 前台开发的系统设计与实现全过程适合数据库课程学生、毕业设计人员或需要撰写课程设计文档的开发者参考。报告按引言、概要设计、数据库分析、详细设计及测试、课程设计心得的顺序组织从系统需求分析、结构设计、功能模块划分入手再深入到用户表、图书类别表、图书表、入库表等数据表结构设计并完整给出存储过程与触发器的实现方式以及系统界面与主要代码设计、功能整体链接测试等内容。资源为 1 个 doc 格式文档压缩包约 229KB涵盖完整目录结构与多张数据表设计既有数据库建模与安全性设计思路也有前后端联调与问题排查要点。该文档已有 517 人浏览学习尤其适合需要完成 Oracle 课程设计报告、希望获得完整参考框架与撰写范式的读者。1. Oracle数据库课程设计怎么从零到一一份图书管理系统课设报告的完整闭环如果这学期要交Oracle数据库课程设计最缺的往往不是SQL语法而是一份能把需求、表结构、存储过程和前端调用串起来的完整参考。这份图书管理系统课设报告正好踩在这个点上它用Oracle 11g做后台前端接VC开发环境实现了对图书信息的增删改查、出入库和库存管理。光是能直接抄的建表语句、登录存储过程、入库过程和触发器就覆盖了课设报告要求的绝大部分硬指标。比较适合正在做“管理系统类”选题或者被老师要求所有数据操作必须走存储过程的同学。下面我按它的设计顺序把表结构、SQL脚本和几个容易翻车的地方逐一拆开讲。2. 先拆系统骨架功能边界、E-R图和五张表的引用关系2.1 功能边界要先定死为什么“增删改查出入库”比借阅系统更容易出活这份课设选的是图书管理系统方向但报告里并没有做借书、还书、超期罚款这类业务而是把范围锁在两块一是对图书信息的增删改查二是图书的入库和出库管理。翻到第2章需求分析原文写得很直白“图书管理系统主要是用Oracle数据库进行逻辑处理实现对图书信息的增删改查以及出库入库的管理。” 这个选题很有代表性——数据库课程设计不考核业务多复杂而是考核你对表、视图、存储过程、触发器这些基础对象的掌握程度。借阅系统要维护借阅状态、惩罚关系至少多两三张关系表排错成本翻一倍。而增删改查加出入库业务流短正好把每一个数据库对象都用上。从课程设计评分角度看这样的功能边界也安全。课设要求里明确写了系统要包含“输入输出、查询、插入、修改、删除等基本功能”所以图书新增、修改、删除、查询、入库这几块已经把必考点全部覆盖。出库在报告正文里没有单独给存储过程但库存表里的StockNum字段就是为出库预留的减库存位你可以在入库存储过程的基础上反向写一个出库过程逻辑完全对称。这里要强调第一点课设不是越复杂越好而是能在规定时间内跑通、能讲清楚每个表为什么存在。再看系统结构。报告里的E-R图只涉及两个核心实体图书和图书类别加上入库和库存这样的操作记录。图书实体包含编号、名称、类别编号、零售价、作者、出版社、库存上下限、描述类别实体包含类别编号和类别名称。库存和入库记录被建模成单独的表而不是直接塞在books表里这一点比很多课设要规范——入库流水归入库表库存快照归库存表books只放图书的基础资料。后面你会看到这种拆分给触发器留了用武之地入库表插入一行触发器自动去更新库存表。在你交报告的时候E-R图是评审老师第一眼看的图。画的时候除了实体和属性一定要把图书和类别之间的1:N关系明确标出来并让外键方向与建表SQL一致。许多同学在PPT里画得很漂亮但建表语句却对不上号结果答辩时被问“这个外键为什么指向那里”直接卡壳。2.2 五张表的职责划分主键怎么选外键往哪指数据类型有哪些坑在3.2节报告给了五张表用户表 yonghu、图书类别表 typ、图书表 books、入库表 InWarehouseitems、库存表 stock。我先用一张表把核心字段和关系列清楚后面建表SQL直接对着使用。表名关键字段主键外键职责说明yonghueno, enameeno—登录用户eno同时存用户IDtypTID, TypeNameTID—图书类别booksISBN, BookName, TID, RetailPrice, Author, Publish, StockMin, StockMax, DescriptionsISBNTID→typ.TID图书主表InWarehouseitemsISBN, BookName, RetailPrice, shuliang—ISBN→books.ISBN入库流水stockISBN, StockNum—ISBN→books.ISBN库存快照重点看两个地方。第一个是主键选择books用ISBN做主键这在图书管理场景里说得通ISBN本身唯一且稳定typ用TID类别编号做主键类型是varchar2(10)。主键长度短外键引用时索引存储开销也小。yonghu的eno是number类型对应前台登录传入的用户ID这块问题不大。但RetailPrice零售价在报告里被定义成了varchar2(10)而不是number。很多课设都这么干因为前台文本框拿到的默认是字符串直接存varchar2可以省一次类型转换。可一旦你要按价格排序、统计总码洋varchar2排序就会出现“9比10大”这种字符串排序的经典错误。我的建议是改成number(8,2)再加一条 check (retailprice 0) 约束。同样入库表的shuliang、库存表的StockNum用了number但没有非空和大于0的约束这也是可以顺手补的点。第二是外键方向。books.TID指向typ.TIDInWarehouseitems.ISBN指向books.ISBNstock.ISBN指向books.ISBN。这个引用方向完全正确类别先存在图书才能引用图书先存在流水和库存才能引用。方向反了或者数据类型不一致比如books.TID是varchar2(10)typ.TID却是number建表时就会报ORA-02291或者ORA-02270这一点第5章避坑部分还会单独展开。2.3 视图bookview把查询逻辑从物理表上剥离开报告3.3节末尾创建了一个简单视图create view bookview as select isbn, bookname, author, publish, retailprice from books;这个视图的作用很直白前台查询图书列表时只需要ISBN、书名、作者、出版社、零售价五个字段把库存上下限和描述字段全部屏蔽掉。底层的books就算以后加了字段比如上架时间只要视图定义不变前端的查询结果集就不会被破坏。视图在课程设计报告里是加分项——很多学生只建表不建视图你有了这个对象就能在报告里多写一段“数据库设计包含视图实现了逻辑数据与物理数据的分离”。参数说明select出来的列名默认继承原表字段名运行后可以在Oracle的USER_VIEWS视图里查看视图文本。如果想要更友好的列名可以在select里用别名例如写成 isbn as book_no。还可以加where条件做成过滤视图比如只显示库存大于0的图书。但有一点要注意基于单表的简单视图在Oracle里默认是可更新的也就是说执行 update bookview set retailprice 59.00 where isbn ... 是合法的它会直接改到底层books表。如果本意是只读视图记得加上 with read only 约束否则前端写错一条语句就可能误改底层数据。3. 建库脚本逐个敲表空间、用户、建表SQL和视图的完整过程3.1 表空间和用户E盘路径、32M起步和autoextend的坑报告3.3节第一步是建表空间原脚本如下create tablespace tushu datafile E:\biaokongjian\tushu.dbf size 32M autoextend on next 32m maxsize 2048m extent management local;这段脚本创建了一个名为tushu的表空间物理文件放在E盘biaokongjian目录下初始大小32M每次用完自动扩展32M上限2048M采用本地管理区段。extent management local是Oracle 10g之后的主流管理方式把区段分配信息存进数据文件本身而不是数据字典高并发下更稳定。这条语句本身没有坑坑在执行前是不是已经建好了E:\biaokongjian目录——Oracle数据文件路径必须真实存在否则直接报ORA-01119。这是刚上手最容易忽视的一点先建目录再建表空间。接下来创建用户create user wsn identified by 1234 default tablespace tushu;创建了一个叫wsn的数据库用户密码是1234。严格说Oracle 11g对密码复杂度是有默认策略的纯数字弱口令在安装了密码校验函数的环境里会被直接拒绝报ORA-28003。遇到这种情况把密码改成字母加数字的组合比如Ws123456或者用SYS账号暂时关掉校验alter profile default limit password_verify_function null;这条命令只建议在本地开发环境临时使用别拿到生产库上去改。建完用户还要授权报告里没有细写但实际跑的时候不给权限连登录都进不去。常见做法是grant connect, resource to wsn; grant unlimited tablespace to wsn;connect允许登录resource允许建表、建视图、写存储过程做课设到这一步就够用了。还有一点和后面的存储过程相关如果过程里写了类似 scott.yonghu 这样的带schema引用你还需要对scott用户的相关对象有访问权限。更稳妥的做法是让wsn在自己的schema下操作SQL直接写表名不要带scott前缀。这一点第5章会单独作为一条避坑记录来讲。3.2 建表SQL和依赖顺序先父表后子表的固定流程表空间和用户就绪后按依赖顺序建表。规则是typ先建books才能引用typbooks先建InWarehouseitems和stock才能引用books。所以报告的建表顺序本质上是外键依赖的拓扑排序。用户表create table yonghu ( eno number primary key, -- 用户编号 ename varchar2(10) -- 用户名 );eno作为用户编号并设为主键number类型足够容纳前台传来的整数ID。ename是用户名用varchar2(10)够用。varchar2存变长字符串实际存几个字符就占用几个字节不像char会固定占满。图书类别表create table typ ( TID varchar2(10) primary key, -- 类别编号 TypeName varchar2(20) not null -- 类别名称 );TID是类别编号主键TypeName是类别名称加了not null。注意TID用varchar2而不用number说明类别编号可能是“A001”这种带前缀的字符串。books表引用TID时外键列的数据类型也必须是varchar2(10)长度不一致会导致外键创建失败。图书表create table books ( ISBN varchar2(20) primary key, -- 图书编号 BookName varchar2(40) not null, -- 名称 TID varchar2(10), -- 类别编号 RetailPrice varchar2(10) not null, -- 零售价 Author varchar2(20), -- 作者 Publish varchar2(30), -- 出版社 StockMin number not null, -- 库存下限 StockMax number not null, -- 库存上限 Descriptions varchar2(100), -- 描述 constraint fk_books_typ foreign key (TID) references typ(TID) );books是整张设计的中心表。外键名称我建议显式写成fk_books_typ好处有两个删除约束时可以 drop constraint fk_books_typ不用去数据字典里查系统自动生成的约束名报告里也更容易写清楚哪张表的哪个外键引用了谁。TID列在books里没有加not null意味着允许图书暂时不分类这是合理的业务弹性。入库表和库存表create table InWarehouseitems ( ISBN varchar2(20), BookName varchar2(40) not null, RetailPrice varchar2(10) not null, shuliang number, -- 入库数量 constraint fk_inwh_books foreign key (ISBN) references books(ISBN) ); create table stock ( ISBN varchar2(20), StockNum number, -- 库存数量 constraint fk_stock_books foreign key (ISBN) references books(ISBN) );InWarehouseitems和stock都没有单独设置主键。入库表没有主键意味着同一本ISBN可以插入两条完全相同的入库记录这在课设里不算致命但严格说是一个设计缺口。建议给InWarehouseitems加一个流水号字段 rkid number primary key用序列加触发器生成stock表则可以直接把ISBN设为主键因为库存表一行只应该对应一本书。改法很简单建表时把 ISBN varchar2(20) 改成 ISBN varchar2(20) primary key 即可。执行顺序再强调一遍先typ再books最后InWarehouseitems和stock。顺序错乱时Oracle会报ORA-02449提示外键依赖的表不存在。如果用的是带脚本批量执行的工具记得手动按顺序选中执行不要全选一把梭。3.3 视图的取舍哪些列适合放进查询视图报告里bookview只选了基础信息五列这是课设的标准粒度。实际上你可以根据自己的前端需求灵活调整这个视图库存查询场景就做库存视图数据核对场景就做包含库存上下限的视图。比如扩展成一个带类别名称的连接视图create or replace view v_book_detail as select b.isbn, b.bookname, b.author, b.publish, b.retailprice, t.typename, b.stockmin, b.stockmax from books b left join typ t on b.tid t.tid;这里用left join而不是inner join是因为inner join只能查出已成功分类的图书而left join会把未分类的图书也保留下来typename显示为null。对管理员来说能看到未分类图书本身就是一种数据异常提示。创建视图后可以在SQL*Plus里用 desc v_book_detail 验证列名和类型。如果后面要覆盖同名的视图写成create or replace view就好不要先drop再create——两个语句分开执行遇到视图正被其他会话引用时可能会长时间锁住。视图列来自多张表时并不需要额外权限只要底层表的访问权限正常即可。假如前端只需要查数据、绝不允许改数据给视图加上 with read only把它变成真正的只读视图省得误操作。4. 存储过程和触发器实战登录、入库和库存同步的正确打开方式4.1 登录存储过程dengluflag返回值的调用约定报告3.4节给出了登录存储过程核心代码是create or replace procedure denglu ( flag out number, -- 0用户不存在 1密码错误 2登录成功 username varchar2, -- 用户名 upwd number -- 用户编号当作密码用 ) as i varchar2(20); p number; begin flag : 0; select t.ename into i from scott.yonghu t where t.ename username; if i is not null then flag : 1; select t.eno into p from scott.yonghu t where t.ename username and t.eno upwd; if upwd is not null then flag : 2; -- 登录成功 else flag : 1; -- 密码不正确 end if; else flag : 0; -- 用户不存在 end if; commit; exception when no_data_found then rollback; end;flag作为out参数调用方通过它区分三种结果0用户不存在1用户存在但密码不正确2登录成功。期间两次查询同一条用户表数据第一次确认用户名是否存在第二次用enocode匹配密码这是很典型的课设写法。但这段过程里藏着一个明显的逻辑bug它判断登录成功的条件写成了if upwd is not null也就是说只要调用时传入的第三个参数不是null它就直接返回flag2并没有真正比对p是否等于upwd。实际效果是无论密码对不对只要传了个非空值就算登录成功。很多课设代码都有这种判断上的毛病——因为课程设计评分的重点是“有存储过程、有out参数、能跑通”逻辑不严谨恰好留出了你可以改进的地方。如果要改成真正的密码校验把过程体中带eno条件的select into继续保留然后判断p是否为空或者是否匹配而不是判断upwd是不是null。改动只有三四行报告里可以体现为“对原课设代码的改进点”。另外要注意嵌套的select into如果没有返回行Oracle会抛no_data_found异常这里用exception when no_data_found then rollback兜底防止整个过程直接崩溃。但rollback会回滚整个事务如果你在登录之外还需要保留其他操作建议改成when others then flag : 0。还有一个硬伤是scott前缀。数据库用户wsn创建的yonghu表被过程写成了scott.yonghu。换个数据库实例scott用户可能不存在过程直接编译报错。正确做法是在自己的用户下执行存储过程SQL写成 from yonghu 就行。4.2 入库存储过程rk先查后写的并发隐患入库过程的原文如下create or replace procedure rk ( isb varchar2, -- 图书编号 bname varchar2, -- 书名 rp varchar2, -- 零售价 sl number -- 入库数量 ) as i number; begin select count(*) into i from inwarehouseitems where isbn isb; if (i 0) then update inwarehouseitems set shuliang shuliang sl where isbn isb; else insert into inwarehouseitems values (isb, bname, rp, sl); end if; end;逻辑很直接先统计入库表里有没有这本ISBN的记录有就累加数量没有就插入新记录。过程本身能跑通但它属于典型的“先查后写”模式存在一个并发窗口两个会话同时调用rk第一次查询都查不到该ISBN于是都走insert分支产生两条重复入库记录或者一边查询一边被另一个会话更新数量互相覆盖。课设里不出问题是因为单机、单用户、串行调用并发根本不会发生。想让这个过程更稳健可以把count判断改成merge语句由数据库引擎自己判定匹配与否merge into inwarehouseitems t using (select isb as isbn from dual) s on (t.isbn s.isbn) when matched then update set t.shuliang t.shuliang sl when not matched then insert (isbn, bookname, retailprice, shuliang) values (isb, bname, rp, sl);merge把判断和操作合成一条语句并发窗口比先count再insert小得多。如果你在报告心得里写一句“考虑到并发问题将入库过程改为merge实现”这个改进点在答辩时是加分项。4.3 触发器chaur入库表每行变更如何同步到库存同步库存的触发器是报告的另一个重点create or replace trigger chaur after insert or update on InWarehouseitems referencing old as old new as new for each row declare n_count number(4); begin if updating or inserting then select count(*) into n_count from stock where isbn :new.isbn; if n_count 0 then update stock set stocknum stocknum :new.shuliang where isbn :new.isbn; else insert into stock(isbn, stocknum) values (:new.isbn, :new.shuliang); end if; end if; end;触发时机是在InWarehouseitems表发生insert或update之后对每一行变更执行一次。referencing子句把旧值和新值分别命名为old和new在行级触发器中用:new.isbn、:new.shuliang取新值。逻辑是先看stock里有没有对应ISBN有就累加库存没有就插入新的库存记录。这里有两个坑要提前预防。第一for each row是行级触发器的标志如果删掉它触发器就是语句级触发器里面使用:new、:old会直接编译报错。第二触发器里如果同表依赖容易形成递归触发本例中InWarehouseitems和stock是两张表不存在这个问题。但如果你后续加了出库表OutWarehouseitems想写一个同样的同步触发器不要复制这段改个表名就完事——出库应该是减少库存你要把update语句改成 set stocknum stocknum - :new.shuliang还要考虑库存不能减成负数先加一层判断。还有一个小细节这个触发器在delete时不会触发因为声明里只写了insert or update。如果未来支持删除入库单做冲销可以把声明改成 after insert or update or delete on InWarehouseitems然后在正文里补一个delete分支把库存减去:old.shuliang。5. Oracle课设避坑清单五条最容易让复现翻车的记录5.1 现象插入图书报ORA-02291外键约束违反建完表后向books插入一条图书记录执行到一半Oracle报ORA-02291提示外键约束违反具体到约束名是fk_books_typ。原因是books.TID通过外键引用typ.TID而typ表里此时还没有对应的类别记录外键约束不允许引用不存在的父记录。解决方法是调整插入顺序先插入typ类别数据再插入books图书数据。如果是已经混入脏数据可以先临时禁用外键灌完数据再启用alter table books disable constraint fk_books_typ; alter table books enable constraint fk_books_typ;注意enable时如果数据里存在孤儿记录会报错并拒绝启用。所以灌数据前先确认TID都有对应类别。5.2 现象编译登录存储过程报ORA-00942表或视图不存在在SQL*Plus或者PL/SQL Developer里编译denglu存储过程提示ORA-00942表或视图不存在定位到from scott.yonghu这一行。原因是当前连接用户wsn没有访问scott.yonghu的权限或者当前数据库实例根本没有scott这个用户。报告里把表名写了scott前缀但在你自己的库里这个schema未必存在。解决方法是把存储过程里的scott前缀去掉直接在wsn用户下写成from yonghu。如果系统里确实需要从别的schema取数正确做法是先授权再引用grant select on scott.yonghu to wsn;但课设没必要搞跨schema所有表建在wsn自己的用户下SQL里不带前缀就是最不容易出错的姿势。5.3 现象创建用户报ORA-28003密码不符合复杂度执行 create user wsn identified by 1234 时Oracle直接报ORA-28003提示密码太简单。原因是Oracle 11g默认的密码校验函数对纯数字弱口令有限制。在一些教学环境里这个校验函数被装到了default profile上所有新建用户都要满足复杂度要求。解决方法是把密码改成字母加数字的组合比如Ws123456重新创建用户或者用SYSDBA登录把校验临时关掉再建库alter profile default limit password_verify_function null;关掉后重新create user就行。这只是课设环境图省事生产库别这么干。5.4 现象建表空间报ORA-01119数据文件创建失败执行create tablespace时Oracle报ORA-01119后面通常还跟着一句操作系统层面的错误信息比如目录不存在。原因是数据文件路径E:\biaokongjian\tushu.dbf里的biaokongjian目录在E盘根本不存在。Oracle不会自动创建目录路径必须事先存在。解决方法是先打开资源管理器把E:\biaokongjian建好再重新执行建表空间语句。或者干脆把路径换成Oracle安装目录下的已有路径例如C:\oracle\oradata\orcl\tushu.dbf。注意路径里的反斜杠在Oracle字符串里会被当作普通字符处理不需要额外转义。5.5 现象按价格排序结果全乱9排在10后面对图书按RetailPrice排序发现结果不符合数值大小顺序“9”排在“10”后面价格字段好像没按数字排。原因是RetailPrice是varchar2(10)类型数据库排序按字符串的字符序进行而不是数值序。字符序里“1”比“9”小所以“10”排在“9”前面。解决方法有两个。彻底的做法是把RetailPrice改成number(8,2)一劳永逸。临时做法是查询时做类型转换select isbn, bookname, to_number(retailprice) as price from books order by to_number(retailprice);to_number在数据量不大时没问题但字段里一旦混入非数字字符就会报ORA-01722。这也是我坚持改字段类型的原因——varchar2存数字短期省事长期全是坑。6. 让课设从“能跑”变成“能讲”三个改造方向和一个自检习惯6.1 用触发器把StockMin变成预警线books表里已经设计了StockMin和StockMax但报告只把它们当字段存着没有实际使用。你可以补一个低库存预警触发器让库存低于下限时输出提示create or replace trigger low_stock_warning after insert or update of stocknum on stock for each row declare v_min books.stockmin%type; begin select stockmin into v_min from books where isbn :new.isbn; if :new.stocknum v_min then dbms_output.put_line(预警ISBN || :new.isbn || 库存低于下限); end if; end;这段触发器在stock表插入或更新库存量后触发先从books查出该ISBN的库存下限再比较新库存是否低于下限。dbms_output.put_line会在SQL*Plus客户端打印提示课设演示时能看到直观效果。如果你想做完整预警还可以在books表里加一个状态字段低于下限自动置为“待采购”这里就不再展开了。6.2 登录过程的密码存取别太原始课设里denglu过程直接用eno当密码甚至不校验密码内容这在演示时可以蒙混过关但答辩时容易被追问。低成本改进是密码字段单独成列不要复用用户编号存储过程里做参数化比较不要在前台拼SQL字符串。更进一步密码存hash而不是明文Oracle里有DBMS_CRYPTO可以算HASH但课设不需要走到那一步你只要在报告里写出“密码字段独立存储且不走明文”这个思路已经比大部分同学严谨了。6.3 一个收尾习惯每次改完库结构先查约束状态从那以后我每接手一份课设数据库都先跑一遍自检SQL把外键状态和视图声明过一遍再往下改select constraint_name, status from user_constraints where constraint_type R; select view_name, text from user_views;第一条查出所有外键约束及启用状态第二条列出当前用户下所有视图的定义文本。跑完这两条语句基本能确认库结构没有半吊子状态再去做功能测试就有底了。这个习惯帮我省掉了大量来回编译的时间希望帮到你。本文还有配套的精品资源点击获取
返回列表