简介:一份面向数据库管理与开发方向学生及IT从业者的Oracle数据库课程设计文档,以‘学生考勤系统’为例,完整呈现从背景分析、需求分析到E-R模型、数据字典、数据库表逻辑结构、表空间与表设计的全过程。需求部分覆盖学生、任课老师、班主任、院系领导、学校领导、系统管理员等不同角色,并涉及请假申请、考勤记录、数据统计、权限管理等模块;设计部分还包含存储过程、触发器、性能优化与备份恢复等实践要点。文档按课程设计报告结构组织,含目录、图表与心得体会,既可作为课程设计模板直接参考,也能帮助读者系统掌握Oracle数据库的规划设计与SQL实现思路。资源包共1个doc文档,合计227KB,内容精炼;已有965人学习/浏览,对于需要完成数据库课程设计或提升Oracle项目实践能力的学生具有较高借鉴价值。
1. Oracle数据库课程设计:从“什么都能做”到“交得出、讲得清”的落地路线
很多人把Oracle数据库课程设计当成一次“从零写一个系统”的练习,结果一上来就建表,做到中期发现实体关系没理顺,只能推翻重来。实际上这门课真正拉开分数的不是界面漂不漂亮,而是表结构、约束、存储过程和演示逻辑能不能自圆其说。这篇笔记按“需求梳理 → 关系建模 → 建表 → PL/SQL → 排错 → 答辩验证”的顺序,把每一步拆成可以直接抄的作业。适合正在做课程设计的学生,也适合要带着学生走完流程的开发者。全程只用Oracle数据库的基础能力,不引入任何额外框架。
2. 需求梳理先行:实体、关系与业务规则怎么落到Oracle模型里
课程设计最常见的失败点,是需求只停在“做一个图书管理系统”这一句话上。这句话没法建表。你需要把它拆成实体、属性、关系、业务规则四类信息。花一天时间做这件事,后面建表、写存储过程会顺利得多。这一步不产出SQL,但产出比SQL更重要的设计文档。
2.1 选题怎么选:评分维度与工作量估算
课程设计的选题一般来自题库,也可以自拟。常见的有图书管理、学生选课、商品进销存、医院门诊、车辆维修。选哪个差别不大,关键看这个题目能不能支撑足够多的约束和存储过程。我一般会按四个维度评估选题:核心实体数量、核心关系数量、非主键业务规则条数、需要输出的数据口径。
核心实体控制在4到6个。少于4个,关系图太单薄;多于6个,一个人不太可能在两周内把每个实体的约束和存储过程都写完整。核心关系一般有2到4条,例如学生和课程之间的选课、读者和图书之间的借阅,这类关系表是存储过程的主要落点。业务规则最好有10条以上,包括唯一性、取值范围、数量上限、状态流转等,这些是CHECK、UNIQUE、外键和触发器存在的理由,也是答辩时的设计亮点。
| 维度 | 及格线 | 加分线 | 工作量风险 |
|---|---|---|---|
| 核心实体 | 4个 | 6个 | 超过6个会明显挤压后续排错时间 |
| 核心关系 | 2条 | 4条 | 关系超过4条,外键与事务边界变复杂 |
| 业务规则 | 10条 | 15条以上 | 规则不足会让PL/SQL部分无内容可写 |
| 数据量要求 | 每表100行 | 10000行以上 | 大数据量才能体现索引与SQL调优价值 |
如果题目本身把实体数量定死了,也没关系,可以通过视图加统计报表补足工作量和复杂度。选题阶段的目标不是“看着高级”,而是“你讲得清、做得完”。这个思路能避免后期推倒重来。
2.2 需求文档的落地格式:实体、属性、关系三张表
需求文档不要求写长篇自然语言,我习惯用三张表把它结构化。第一张是实体表:列出实体名、主键、一句话描述。第二张是属性表:列出每个实体下的字段名、类型、是否必填、默认值、取值约束。第三张是关系表:列出关系名、参与实体、基数、是否独立成表。
以图书借阅为例,实体表大致是:
| 实体 | 主键 | 描述 |
|---|---|---|
| reader_info | reader_id | 读者,包含可借数量和上限 |
| book_info | book_id | 图书,包含在馆状态 |
| borrow_record | record_id | 借阅记录,一次借阅对应一行 |
属性表我一般只列关键字段,否则文档会很臃肿。比如reader_info的reader_no需要唯一,borrow_limit取值在1到30之间;book_info的status只用B和L两个值,分别表示在馆和借出;borrow_record的borrow_date默认SYSDATE,return_date为NULL表示未还。把这些写清楚,建表时就能直接翻译成SQL。
关系表记录基数关系,例如reader_info与borrow_record是1对N,book_info与borrow_record也是1对N,category与book_info是1对N。承载方式要么是独立表,要么只是外键。这三张表就是ER图的数据来源,也是后面CREATE TABLE语句的清单。很多同学直接打开图形工具画ER图,画得挺漂亮,但问到一个字段的类型和默认值就答不上来,原因就是ER图表达不了约束细节,三张表可以。
2.3 把业务规则翻译成约束清单
业务规则是课程设计最容易出彩、也最容易踩坑的地方。不要在文档里写“读者不能超过借阅上限”这种话,要把它翻译成可以实施的Oracle约束。下面是一张规则到实现的对照表,可以直接用于设计文档。
| 业务规则 | 实现方式 | 注意点 |
|---|---|---|
| 读者编号唯一 | UNIQUE约束 | 在表里直接建 |
| 性别取值固定 | CHECK约束 | Oracle会按行校验,跨行校验不能靠CHECK |
| 同一读者同一本书只能有一条未归还记录 | 唯一约束加函数索引 | 因为return_date为NULL的多行不影响普通唯一约束,需要基于“未归还”构造表达式 |
| 借书时读者是否存在 | 外键 | 外键能防不存在,但不能防被禁用 |
| 逾期天数自动算罚款 | 存储过程或视图 | 尽量用视图呈现,不要存金额 |
| 图书状态与借阅记录一致 | 触发器 | 借出后自动改状态,避免应用层漏改 |
这里有个容易被忽略的点:Oracle的唯一约束把NULL看作“不参与比较”。同一个读者的两条借阅记录如果都未归还,return_date都是NULL,普通唯一约束(reader_id, book_id, return_date)不会拦住第二行。我一般用函数索引截断:
CREATE UNIQUE INDEX ux_borrow_unreturned ON borrow_record(CASE WHEN return_date IS NULL THEN reader_id || '#' || book_id END);逻辑说明:当return_date为NULL时,CASE产生一个字符串,索引强制唯一;已经归还的行返回NULL,不参与唯一判断。参数说明:字符串连接用两个竖线,分隔符尽量用不会出现在字段里的字符,防止不同读者产生碰撞。这段代码在课程设计里属于亮点,答辩时大概率被追问,一定要自己推演一遍。
3. 建表与约束:把ER图翻译成Oracle表结构的最小代码集
需求清单就位后,建表就不是照着感觉敲命令,而是按实体表、属性表、关系表逐张落SQL。这一章给出最小可抄的建表代码,同时说明类型、主键、约束和索引的设计取舍。记住:Oracle的建表和图形工具自动生成不一样,你写的每一行都要能在答辩时解释。
3.1 规范化与适度冗余:先定结构的边界
课程设计要求规范化,但规范化不是越高越好。一般到第三范式就够了。高于第三范式会造成大量JOIN,成绩并不会因此更高;低于第二范式会出现重复存储,更新时容易产生不一致。
我自己的判断标准:每个非主键属性都完全依赖主键,不依赖主键的一部分;没有传递依赖。在这个前提下,如果某个查询频繁且JOIN代价高,我会额外冗余一列。举例来说,borrow_record表里存book_name。严格来说,book_name依赖book_id,不直接依赖record_id,是一种传递依赖。但借阅记录列表经常要显示书名,冗余一列可以少JOIN一次。
代价是book_info里的书名改了,borrow_record里的旧值不会自动变。解决办法有两个:一是把“修改书名”做成存储过程,在更新book_info的同时更新borrow_record;二是接受历史快照语义,认为借阅记录保留的是“借出那一刻的书名”。前者复杂,后者在业务上更合理。课程设计里用后者,答辩时一句话就能讲清楚。所以要记住:适度冗余不是错,但要能说明为什么冗余、怎么维护。
3.2 类型、主键与命名:建表前的三个选择题
类型选型的常见做法:字符串用VARCHAR2,不用VARCHAR;定长状态码用CHAR(1)或CHAR(2);数值用NUMBER,金额用NUMBER(10,2);日期用DATE或TIMESTAMP,不要用字符串存日期。逻辑删除标志用CHAR(1)或NUMBER(1),不要用VARCHAR2(20)存“是/否”,否则应用层很难统一处理。
主键我习惯用NUMBER加序列。Oracle 12c之前没有原生的IDENTITY,为了数据库版本兼容,用序列加触发器是通用做法。如果你确认环境是12c及以上,也可以直接用GENERATED BY DEFAULT AS IDENTITY,但课程设计里为了展示“懂旧机制”,序列加触发器更稳。
命名规范上,坚持三条:表名单数加下划线,比如reader_info而不是readers;主键统一叫“实体名_id”,比如reader_id;外键列名与主键列名保持一致,比如borrow_record.reader_id引用reader_info.reader_id。约束命名用前缀:PK_、FK_、UQ_、CK_。这样建表脚本出错时,看约束名就能定位是哪张表哪个字段,这是课程设计文档里能拿分的工程习惯。
3.3 约束和索引:哪些放进表里,哪些单独建
主键和唯一约束建议在CREATE TABLE里直接写,清晰简单。外键也可以直接写在表里,但要注意:外键列上最好单独再建一个普通索引。原因是,删除父表数据或修改父表主键时,Oracle要去子表检查外键,如果外键列没有索引,会锁住子表或做全表扫描。这在数据量大时就是事故。课程设计的数据量一般不大,但答辩老师问到这里,你能答出“外键列加索引”就是加分项。
CHECK约束能覆盖单行内的校验,比如gender IN ('M','F')、borrow_limit BETWEEN 1 AND 30。跨行的业务规则,例如“同一本书同一时间只有一条在馆记录”,CHECK管不了,要用唯一索引或触发器。索引不能随意加,每个索引都会拖慢INSERT和UPDATE。课程设计数据量小,索引数量不用多,我一般只建主键索引、外键索引,以及要在答辩里演示的查询列索引。把一张表的索引控制在3个以内,演示执行计划时更容易讲清。
3.4 可以抄的建表脚本与参数说明
下面是一组最小但完整的建表脚本,覆盖学生选课场景。表结构包括student_info、course_info和选课关系表。先建主表,再建子表,否则外键无法建立。
CREATE TABLE student_info ( stu_id NUMBER(10) NOT NULL, stu_no VARCHAR2(20) NOT NULL, stu_name VARCHAR2(50) NOT NULL, gender CHAR(1) DEFAULT 'M', enroll_date DATE DEFAULT SYSDATE, CONSTRAINT pk_student_info PRIMARY KEY (stu_id), CONSTRAINT uq_student_info_stu_no UNIQUE (stu_no), CONSTRAINT ck_student_info_gender CHECK (gender IN ('M', 'F')) );逻辑说明:stu_id是代理主键,由序列生成;stu_no是业务编号,比如学号,有唯一约束;gender用CHECK限定,默认M。参数说明:NUMBER(10)做主键足够业务规模;VARCHAR2必须带长度,Oracle里不写长度会报错。
CREATE TABLE course_info ( course_id NUMBER(10) NOT NULL, course_no VARCHAR2(20) NOT NULL, course_name VARCHAR2(100) NOT NULL, credit NUMBER(3,1) DEFAULT 0, CONSTRAINT pk_course_info PRIMARY KEY (course_id), CONSTRAINT uq_course_info_course_no UNIQUE (course_no) );逻辑说明:course_id是代理主键,credit用NUMBER(3,1)表示保留一位小数,存储上限是99.9,对课程学分来说足够。
CREATE TABLE enroll_record ( enroll_id NUMBER(10) NOT NULL, stu_id NUMBER(10) NOT NULL, course_id NUMBER(10) NOT NULL, score NUMBER(5,2), enroll_date DATE DEFAULT SYSDATE, CONSTRAINT pk_enroll_record PRIMARY KEY (enroll_id), CONSTRAINT fk_enroll_student FOREIGN KEY (stu_id) REFERENCES student_info (stu_id), CONSTRAINT fk_enroll_course FOREIGN KEY (course_id) REFERENCES course_info (course_id), CONSTRAINT uq_enroll_unique UNIQUE (stu_id, course_id) );逻辑说明:enroll_id是代理主键;stu_id和course_id是两个外键,分别引用学生表和课程表;唯一约束确保一个学生不能重复选同一门课。参数说明:score用NUMBER(5,2),能保存0到999.99的成绩。外键约束在Oracle里默认启用,不需要额外加ENABLE。
外键列索引的创建脚本如下:
CREATE INDEX idx_enroll_stu_id ON enroll_record(stu_id); CREATE INDEX idx_enroll_course_id ON enroll_record(course_id);逻辑说明:这两个索引不是必须的,但推荐建,尤其是数据量插到几千行以后,删除课程或学生时的行为会更可控。参数说明:索引名带表名和列名,便于审核和维护。
4. PL/SQL与存储过程:用事务、触发器撑起课程设计的业务逻辑
很多课程设计文档里,业务逻辑只写在增删改查界面上。但在Oracle课程设计里,最直接的展示方式是存储过程和触发器,因为建表是静态的,存储过程才是体现数据一致性和业务规则的地方。这一章用一个借书场景串起存储过程、事务、序列和异常处理。
4.1 哪些业务逻辑适合放进存储过程
存储过程适合放“要跨多张表、要么全部成功要么全部失败”的操作。比如借书:要检查读者可借数量上限,要插入借阅记录,还要更新图书状态。这三个动作必须是一个事务,不能由应用层分三次提交。另一个典型场景是还书:计算逾期天数、生成罚款金额、更新图书状态。
不适合放存储过程的是纯查询。如果只是按条件查列表,直接用SQL或视图,不要包一层存储过程,否则答辩时很难解释为什么要多这一层。我一般把握一个原则:一个动作只涉及一张表,用普通DML;涉及多张表且有业务判断,才用存储过程。存储过程命名也要能看出用途,比如borrow_book、return_book,不要写p1、p2。参数用p_前缀,局部变量用v_前缀,这是PL/SQL最常见的编码习惯,也方便答辩时读代码。
4.2 借书存储过程:从检查到提交的完整事务示例
下面这个存储过程覆盖了检查读者存在、检查在借数量、检查图书状态、插入记录、更新状态、提交六个动作。
CREATE OR REPLACE PROCEDURE borrow_book( p_reader_id IN NUMBER, p_book_id IN NUMBER ) IS v_borrow_count NUMBER; v_borrow_limit NUMBER; v_book_status CHAR(1); BEGIN SELECT borrow_limit INTO v_borrow_limit FROM reader_info WHERE reader_id = p_reader_id; SELECT COUNT(*) INTO v_borrow_count FROM borrow_record WHERE reader_id = p_reader_id AND return_date IS NULL; IF v_borrow_count >= v_borrow_limit THEN RAISE_APPLICATION_ERROR(-20001, '在借数量已达上限'); END IF; SELECT status INTO v_book_status FROM book_info WHERE book_id = p_book_id; IF v_book_status = 'L' THEN RAISE_APPLICATION_ERROR(-20002, '图书已借出'); END IF; INSERT INTO borrow_record (record_id, reader_id, book_id, borrow_date, return_date) VALUES (seq_borrow_record.NEXTVAL, p_reader_id, p_book_id, SYSDATE, NULL); UPDATE book_info SET status = 'L' WHERE book_id = p_book_id; COMMIT; EXCEPTION WHEN NO_DATA_FOUND THEN ROLLBACK; RAISE_APPLICATION_ERROR(-20003, '读者或图书不存在'); WHEN OTHERS THEN ROLLBACK; RAISE; END borrow_book; /逻辑说明:整个过程在内存里完成所有检查,最后一条DML触发后统一COMMIT,任何一个检查失败都走异常块回滚。NO_DATA_FOUND捕获SELECT不到行的情况;OTHERS分支把原始错误继续抛给调用方,避免吞异常。参数说明:p_reader_id和p_book_id都只读,不写OUT参数,调用方可以通过错误码判断失败原因。RAISE_APPLICATION_ERROR的编号范围是-20001到-20999,不要超出。
4.3 序列与触发器:主键自动填充和状态联动
先看序列:
CREATE SEQUENCE seq_borrow_record START WITH 1 INCREMENT BY 1 NOCACHE;逻辑说明:序列用于主键生成,NOCACHE避免RAC环境下序列跳跃;单机课程设计用NOCACHE更安全。参数说明:START WITH从1开始,INCREMENT BY 1步长,也可以改成CACHE 20提升性能,但课程设计里意义不大。
触发器自动填充主键:
CREATE OR REPLACE TRIGGER trg_borrow_record_bi BEFORE INSERT ON borrow_record FOR EACH ROW BEGIN IF :NEW.record_id IS NULL THEN SELECT seq_borrow_record.NEXTVAL INTO :NEW.record_id FROM DUAL; END IF; END; /逻辑说明::NEW是插入前的行副本,给主键赋值;IF判断避免显式传主键时被覆盖。参数说明:FOR EACH ROW表示行级触发器,BEFORE INSERT表示插入前触发。
触发器的另一个用途是状态联动,即borrow_record插入后自动更新book_info状态。但要注意,如果同一个事务里先插borrow_record再手动更新book_info,触发器可能造成重复更新。所以在课程设计里,我建议只让触发器管主键填充,业务状态在存储过程里明确维护,两者各司其职,答辩时更清晰。
4.4 异常处理:用自治事务日志留住翻车现场
存储过程在出问题时如果直接抛出异常,只能看到错误码和消息,看不到当时的数据。课程设计里加一张错误日志表会很加分:
CREATE TABLE proc_error_log ( log_id NUMBER(10) NOT NULL, proc_name VARCHAR2(100) NOT NULL, err_code NUMBER(10), err_msg VARCHAR2(4000), occur_time DATE DEFAULT SYSDATE, CONSTRAINT pk_proc_error_log PRIMARY KEY (log_id) );要把日志写进这张表而不被回滚,需要自治事务,因为异常分支里的ROLLBACK会把之前的日志也回滚掉。示例:
CREATE OR REPLACE PROCEDURE log_error( p_proc_name VARCHAR2, p_err_code NUMBER, p_err_msg VARCHAR2 ) IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO proc_error_log(log_id, proc_name, err_code, err_msg) VALUES (seq_proc_error_log.NEXTVAL, p_proc_name, p_err_code, p_err_msg); COMMIT; END log_error; /逻辑说明:PRAGMA AUTONOMOUS_TRANSACTION让该过程在独立事务里提交日志,不受外层ROLLBACK影响。然后在borrow_book的EXCEPTION块里调用log_error,再重新抛出原异常。参数说明:p_err_msg如果来自SQLERRM,要注意长度可能超过4000,建议用SUBSTR截断,避免日志表插入失败。
5. Oracle课程设计避坑指南:5个高频翻车点与排查路径
这一章写我做Oracle课程设计时踩过、也看别人反复踩的坑。每一条都按现象、原因、解决来写。前两条是环境问题,后三条是数据与设计问题。如果你正在开发中,可以先略过这章,开始答辩前再回来对照。很多同学在演示现场翻车,几乎都集中在下面这几类问题上。
5.1 中文乱码:现象、原因、解决
现象:INSERT中文后,SELECT看到的是问号或类似“æ±”的乱码;用SQL*Plus查询正常,但集成开发环境里乱。
原因:客户端字符集和数据库字符集不一致。常见组合是数据库字符集为AL32UTF8,而客户端的NLS_LANG设置成ZHS16GBK,或者反过来。也可能是操作系统的语言环境影响了终端,这种情况在Windows和Linux上都有可能出现。
解决:先确认数据库字符集:
SELECT value FROM nls_database_parameters WHERE parameter = 'NLS_CHARACTERSET';然后设置客户端环境。Linux或macOS终端:
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8Windows命令行:
set NLS_LANG=AMERICAN_AMERICA.AL32UTF8参数说明:NLS_LANG由“语言_国家.字符集”组成,关键是字符集部分要和数据库一致。改完客户端后,已经乱码的旧数据不会自动变好,所以要在写数据前改好环境。如果改了客户端还不行,检查一下是不是用了SQL*Loader或外部表,那种情况要确认文件本身的编码。
5.2 权限不足:ORA-01031 与 ORA-01950
现象:建表成功,但创建触发器或存储过程时提示ORA-01031权限不足;往带序列的表插入数据时提示ORA-01950对表空间无权限。
原因:ORA-01031通常是当前用户没有CREATE TRIGGER、CREATE PROCEDURE或相关权限;ORA-01950通常是用户没有对应表空间的配额,不是表空间本身不存在。
解决:用系统管理员账号给当前课程设计账号授权。典型的授权语句如下:
-- 把 demo_user 替换成你的数据库用户 GRANT CREATE SESSION TO demo_user; GRANT CREATE TABLE TO demo_user; GRANT CREATE SEQUENCE TO demo_user; GRANT CREATE PROCEDURE TO demo_user; GRANT CREATE TRIGGER TO demo_user; GRANT UNLIMITED TABLESPACE TO demo_user;参数说明:UNLIMITED TABLESPACE会一次性解决表空间配额问题,但如果你被要求最小权限,可以改成ALTER USER demo_user QUOTA 100M ON USERS。另外,还要检查该用户的默认表空间是否可用,我见过用户被设了只读PROFILE,导致所有DDL失败,这种得先查DEFAULT_TABLESPACE和PROFILE。
5.3 表空间不足:临时段爆满的快速补救
现象:一条普通的SELECT或CREATE TABLE突然报“unable to extend TEMP file”或ORA-01652,而表空间明明还有空间。
原因:临时表空间或用户默认表空间太小,排序、去重、索引创建都需要临时段。也可能是数据文件触发了AUTOEXTEND的MAXSIZE上限。
解决:先查表空间和数据文件:
SELECT tablespace_name, file_name, bytes/1024/1024 AS mb FROM dba_data_files;临时表空间使用:
SELECT tablespace_name, bytes/1024/1024 AS mb FROM dba_temp_files;确认路径后扩大数据文件:
ALTER DATABASE DATAFILE '/u01/app/oracle/oradata/XE/users01.dbf' AUTOEXTEND ON NEXT 50M MAXSIZE 2G;参数说明:NEXT 50M表示每次自动扩展50M,MAXSIZE 2G是上限,路径要替换成你环境里的实际路径。临时表空间还可以用ALTER TABLESPACE temp ADD TEMPFILE新增一个临时文件。遇到这个错误,先检查是不是有超长排序,比如SELECT DISTINCT加多个大字段,如果能缩小查询范围,就不要一味扩表空间。
5.4 死锁与阻塞:数据没提交导致的悬案
现象:两条UPDATE语句互相等待,数据库会话卡住;或者一个简单的UPDATE迟迟不返回。课堂演示时最尴尬的就是在这个位置卡住。
原因:Oracle的写操作会锁行。如果A会话更新了一行没提交,B会话再更新同一行就会等待。更麻烦的是,A又去更新B已经锁住的行,形成死锁。死锁中Oracle会主动让一方失败,但如果是单方向阻塞,则可能无限等待。
解决:先找到阻塞会话:
SELECT blocking_session, sid, serial#, event, wait_class FROM v$session WHERE blocking_session IS NOT NULL;如果确认是事务没提交,和同组同学确认后可以断开会话:
ALTER SYSTEM KILL SESSION '123,456' IMMEDIATE;参数说明:123和456来自上面的查询结果,两个数字用逗号分隔。注意,KILL SESSION只是断开连接,不解决业务数据是否提交的问题。遇到这个坑要养成两个习惯:一是存储过程里不要在一个循环内频繁COMMIT;二是任何演示前,确认所有旧事务都已经执行COMMIT或ROLLBACK。
5.5 数据导出导入:版本与字符集不一致的后患
现象:用expdp导出的dmp文件在另一台环境导入时,提示字符集不一致或对象已存在;用imp导入时提示ORA-31684。
原因:不同库的NLS_CHARACTERSET不同,或者导出时用了全库模式,目标端已有同名对象。
解决:课程设计里一般只导出自己的用户数据,命令如下:
expdp demo_user/password@localhost:1521/XE SCHEMAS=demo_user DIRECTORY=DATA_PUMP_DIR DUMPFILE=design.dmp LOGFILE=expdp_design.log导入前确认目标库字符集。如果目标库是ZHS16GBK而导出的是AL32UTF8,要先用SQL*Plus处理或重新创建数据库,不要指望expdp自动转换。另一个常见做法是只导出数据不导出表结构,用CONTENT=DATA_ONLY参数,但目标端结构必须提前存在。
参数说明:SCHEMAS指定用户,DIRECTORY要指向Oracle系统里的目录对象,不是操作系统路径。需要先执行CREATE DIRECTORY并授权,否则expdp会直接报错。如果课程设计只在一台机器上演示,导出导入不是必做项,但要交到服务器上评阅时,提前两天做一次演练,别等答辩当天处理数据迁移。
6. 答辩前夜:用验证SQL、演示脚本和追问预案把分数稳住
答辩前夜别再去加新功能,把已有的东西验证一遍。我习惯先跑三条SQL。第一,检查空表和异常数据:
SELECT COUNT(*) FROM enroll_record WHERE stu_id IS NULL OR course_id IS NULL;第二,验证外键约束真的生效:往关系表插入一个不存在的stu_id,预期是会收到外键报错,而不是插入成功。这个失败案例能瞬间证明外键不是摆设。第三,调一次存储过程的自定义异常分支:
BEGIN borrow_book(p_reader_id => 1, p_book_id => 999); END; /预期是“读者或图书不存在”这类自定义错误,而不是ORA-02291原生错误码。这能证明异常处理是自己写的。
演示顺序我一般按“建表 → 查看约束 → 正常增删改查 → 存储过程 → 触发器 → 失败案例”来设计。失败案例要主动演示,比如借一本已经借出的书,展示自定义异常信息。很多同学不敢演示异常,怕露怯,实际上异常处理是加分项,比一路顺演更能体现工程意识。
最后准备三个高频追问:为什么用序列而不是直接写主键?为什么把某条规则放在存储过程而不是触发器?为什么用外键而不是在应用层控制?回答原则就是讲清楚“放在哪个层做、为什么”。我个人的习惯是,答辩前夜把存储过程里每个RAISE_APPLICATION_ERROR的触发分支都手动跑一遍,成功和失败两条路径都记录结果。这样无论老师从哪个分支追问,都不会把过程当黑匣子。希望帮到你。
本文还有配套的精品资源,点击获取