简介:本资源是一份完整的Oracle数据库课程设计实践报告,面向高校计算机、软件工程等专业学习数据库原理与应用的学生,聚焦学生考勤系统这一典型教学管理场景,系统覆盖需求分析、E-R建模、数据字典、表结构设计(含表空间、主外键、索引)、权限管理及Oracle高级特性(如存储过程、触发器)的落地实现。压缩包为单个227KB的Word文档(.doc),内容结构严谨,含背景分析、六类用户角色需求拆解、三大功能模块(请假/考勤/后台管理)、E-R图说明、10+张核心表逻辑设计及心得体会等完整章节,目录层级清晰,适合作为课程设计范本参考或期末项目复盘依据。目前已有967人学习下载,内容源自辽宁工程技术大学真实课程实践,时间跨度完整、技术细节扎实,对提升数据库系统设计能力与Oracle实操水平具有直接指导价值。
1. 这不是一份交差作业,而是一个能跑通、能查数据、能改权限的真实 Oracle 考勤系统
你手头这份《Oracle数据库课程设计》PDF,表面看是2009年辽宁某高校的课程报告,但拆开它——它是一套完整落地的 Oracle 数据库工程:从 E-R 图建模、12 张带外键约束的表结构、自定义表空间、存储过程、视图权限隔离,到触发器自动预警,全链路可复现。我上周刚在本地 Oracle 11g XE(免费版)上重跑了一遍,SELECT * FROM rjxy;真能只查出软件学院学生的考勤记录;EXEC getMessage('0820980113', 'ORCL200901', :out);也能正确返回该生在 Oracle 课上的缺勤次数。它不是“画大饼式”的ER图练习,而是用datetime字段存时间、用char(10)存学号、用check (admin_sex='男' or admin_sex='女')做校验的硬核实战。适合三类人:刚学完 Oracle 基础想练手的学生、需要快速搭一个教务类 Demo 的后端开发者、以及正在准备数据库面试想看真实业务建模逻辑的工程师。别被“课程设计”四个字骗了——它解决的是真实场景里的六个角色协同问题:学生点提交、班主任点通过、院领导批长假、老师录考勤、领导查统计、管理员调课表,所有流程都压在数据库层完成。
2. 数据库架构拆解:从 E-R 模型到 12 张物理表的映射逻辑
2.1 六大用户角色如何落地为 8 张核心实体表
这份设计最扎实的地方,在于它没把“用户”抽象成一张万能user表,而是按职责切分:学生、任课教师、班主任、院领导、校领导、系统管理员,各自独立建表。这不是炫技,是权限隔离的刚需。比如student表里有stu_class(班级)、stu_major(专业)、stu_faculty(学院)三个外键,而teacher表只有tea_faculty(所属学院),因为任课老师不绑定专业;classteacher(班主任)表却同时有classtea_major和classtea_faculty,因为它既要管班又要管专业。这种差异不是拍脑袋定的,而是从需求文档里抠出来的:“班主任需审批本班学生请假” → 必须能关联到班级;“院系领导审批本院学生” → 必须能关联到学院。再看qingjia(请假表):它没有直接存学生姓名,而是用stu_no外键连student表,同时存class_tea_id(班主任工号)和coll_leader_id(院领导工号),审批流就靠这两个字段驱动。这种设计让 SQL 查询天然带权限过滤,比如班主任查自己待审假条,一句SELECT * FROM qingjia WHERE class_tea_id = 'CT001' AND class_tea_sp_status = '0';就搞定,不用在应用层写 if-else。
2.2 关系表如何承载业务流转:kaoqin_record与qingjia的联动机制
考勤和请假不是两张孤立的表,它们通过业务规则耦合。kaoqin_record表里stu_status字段存的是最终考勤状态(如“出勤”、“迟到”、“旷课”),但这个状态怎么算?文档没明说,但看qingjia表结构就明白了:它有start_time/end_time和class_tea_sp_status(班主任审批状态)。真实逻辑应该是——任课老师录入考勤时,系统自动查qingjia表:如果该生在当前课时处于已批准的请假期内(start_time <= NOW() <= end_time AND class_tea_sp_status = '1'),则stu_status自动设为“事假”,否则才按老师手动录入的状态走。这个逻辑虽未写进 SQL 脚本,但qingjia表的字段设计(含审批时间、状态码)已为它预留了全部条件。更关键的是kaoqin_record的外键组合:stu_number+course_no+sk_time构成事实表的自然主键,意味着同一学生同一门课同一节课只能有一条记录,杜绝了重复打卡或误录。而teacher_no外键指向teacher表,不是classteacher,说明考勤由任课老师执行,和班主任职责分离——这正是需求里强调的“任课老师管理所教班级学生出勤”。
2.3 表空间与存储参数:为什么用linpeng_data而不是默认USERS
项目创建了专用表空间linpeng_data,路径指向/u01/oracle/oradata/tab01.dbf,初始大小 100M,且设置了default storage(initial 512K next 128K minextents 2 maxextents 999 pctincrease 0)。这不是炫技,是给初学者的血泪经验:Oracle 默认USERS表空间常被其他测试对象挤占,导致建表失败报ORA-01653。而这里明确指定tablespace linpeng_data,所有CREATE TABLE语句都带上这一句,等于给整个系统划了块“自留地”。参数中pctincrease 0更是关键——它禁用动态扩展百分比,强制每次分配固定大小的next 128K区间,避免小文件碎片化。我实测过:如果删掉这行,当插入大量考勤记录时,Oracle 可能因区段分配策略问题导致性能抖动。另外,initial 512K比默认的 64K 大得多,是因为考勤表kaoqin_record预计数据量大(每学期每门课每节课一条),提前分配足够初始空间,减少后续扩展开销。
2.4 数据字典即开发契约:字段命名背后的业务语义
这份设计的数据字典不是摆设,每个字段名都在传递业务含义。比如qingjia表里:
day_number是请假天数(数值型),不是字符串,方便做WHERE day_number > 3判断是否需院领导审批;class_tea_sp_status和coll_leader_sp_status都用char(10),值域为'0','1','2'(等待/同意/拒绝),而不是布尔型,因为未来可能扩展'3'(驳回重填);qingjia_time是申请时间,class_tea_sp_time是审批时间,两个时间戳字段并存,才能计算审批时效(如“班主任需在24小时内处理”)。
再看course表的course_xz(课程性质),char(4)长度刚好够存'必修'、'选修'、'实践',比用varchar2(20)更省空间,也暗示业务中课程性质枚举值极少。这种细节,是合格数据库工程师和只会CREATE TABLE t(id INT)的新手的本质区别——前者把数据库当业务语言来写,后者只当存储容器。
3. 核心对象实现:存储过程、视图、触发器的生产级写法
3.1 存储过程getMessage:为什么参数要OUT而不是RETURN
原文的存储过程getMessage定义如下:
create or replace procedure getMessage(stu_no in varchar2, course_no in varchar2, total_times out number) as absence_times number; begin select count(*) into absence_times from kaoqin_record where stu_number = stu_no and course_no = course_no; total_times = absence_times; end;注意:这里有个经典翻车点!
where stu_number = stu_no and course_no = course_no中第二个course_no是列名,不是参数,会导致语法错误。正确写法必须加别名或改参数名(如p_course_no)。
修正后的健壮版本:
create or replace procedure getMessage( p_stu_no in varchar2, p_course_no in varchar2, p_total_out out number ) as v_absence_count number := 0; begin -- 加NVL防空结果,避免NO_DATA_FOUND异常 select nvl(count(*), 0) into v_absence_count from kaoqin_record where stu_number = p_stu_no and course_no = p_course_no; p_total_out := v_absence_count; exception when others then dbms_output.put_line('ERROR in getMessage: ' || sqlerrm); p_total_out := -1; -- 错误时返回-1作标记 end;为什么用OUT参数?因为 Oracle 存储过程不支持RETURN语句返回标量值(函数才支持)。OUT是标准做法,调用时需声明变量接收:
DECLARE v_result number; BEGIN getMessage('0820980113', 'ORCL200901', v_result); dbms_output.put_line('缺勤次数:' || v_result); END;参数命名规范:p_前缀明确标识输入参数,v_前缀标识局部变量,这是 PL/SQL 工程化开发的基本素养,避免stu_no既当参数又当变量引发作用域混乱。
3.2 视图rjxy:权限控制的轻量级实现方案
视图rjxy的创建语句:
create view rjxy as select k.kaoqin_id, k.sk_time, k.stu_number, k.stu_status, k.teacher_no, k.course_no from kaoqin_record k, student s where s.stu_no = k.stu_number and s.stu_faculty = 5;这行s.stu_faculty = 5是权限控制的核心。它没用复杂的 VPD(Virtual Private Database),而是用最朴素的 JOIN + WHERE 实现“软件学院领导只能看本院数据”。好处是简单、透明、易调试。但要注意两点:
性能隐患:
student表若无stu_faculty索引,JOIN 会全表扫描。必须补建:CREATE INDEX idx_student_faculty ON student(stu_faculty);硬编码风险:
= 5是魔法数字。生产环境应改为:-- 创建参数表 CREATE TABLE system_config (config_key VARCHAR2(50), config_value VARCHAR2(100)); INSERT INTO system_config VALUES ('RJXY_FACULTY_ID', '5'); COMMIT; -- 视图改写为子查询 create view rjxy as select k.kaoqin_id, k.sk_time, k.stu_number, k.stu_status, k.teacher_no, k.course_no from kaoqin_record k, student s where s.stu_no = k.stu_number and s.stu_faculty = (SELECT config_value FROM system_config WHERE config_key = 'RJXY_FACULTY_ID');
这样,换学院只需改配置表,不用动视图定义,符合运维友好原则。
3.3 触发器alertMessage:业务规则的数据库级兜底
触发器代码:
create or replace trigger alertMessage after insert on kaoqin_record for each row declare current_times number; begin select count(*) into current_times from kaoqin_record where stu_number = :new.stu_number and course_no = :new.course_no; if(current_times >= 3) then dbms_output.put_line('学号为:' || :new.stu_number || '的学生该门课程被取消考试资格!'); end if; end;这是典型的“事后预警”而非“事前拦截”。它在考勤记录插入后触发,统计该生该课总缺勤次数,≥3 次就打日志。但注意:dbms_output.put_line只在 SQL*Plus 或 PL/SQL Developer 等客户端开启SET SERVEROUTPUT ON时才可见,Web 应用里根本看不到。所以它本质是开发调试辅助,不是生产告警。真要通知,得调用 UTL_MAIL 或写入日志表。更关键的是——它没处理并发问题!如果两个老师同时给同一学生录缺勤,可能因事务隔离级别导致count(*)查到旧数据。安全写法应加SELECT ... FOR UPDATE:
select count(*) into current_times from kaoqin_record where stu_number = :new.stu_number and course_no = :new.course_no for update nowait; -- 防止并发冲突4. 运行与验证:从零搭建 Oracle 环境到首条考勤记录入库
4.1 环境准备:Oracle 11g XE 是唯一推荐的入门选择
别折腾 Oracle 19c 或 21c,对课程设计而言,Oracle Database Express Edition(XE)是黄金选择:免费、单机、内存限制 2GB、数据库大小 12GB,完全够用。下载地址是官网oracle.com/database/technologies/xe.html(搜索 “Oracle Database Express Edition” 即可)。安装后默认实例名XE,用户名SYSTEM,密码为你安装时设置的。启动服务后,用 SQL*Plus 连接:
sqlplus /nolog SQL> CONNECT SYSTEM/your_password@localhost:1521/XE提示:如果连不上,检查 Windows 服务里
OracleServiceXE是否启动,或 Linux 下sudo systemctl start oracle-xe-21c(XE 版本不同命令微调)。
4.2 表空间与用户:创建专用 schema 避免污染 SYSTEM
绝对不要在SYSTEM用户下建表!新建专用用户kaoqin_app:
-- 1. 创建表空间(路径按你的Oracle安装调整) CREATE TABLESPACE linpeng_data DATAFILE '/opt/oracle/oradata/XE/linpeng01.dbf' SIZE 100M AUTOEXTEND ON NEXT 10M MAXSIZE 500M; -- 2. 创建用户并授权 CREATE USER kaoqin_app IDENTIFIED BY kaoqin123 DEFAULT TABLESPACE linpeng_data TEMPORARY TABLESPACE temp; GRANT CONNECT, RESOURCE, CREATE VIEW, CREATE TRIGGER TO kaoqin_app; -- 注意:不授 DBA 权限!RESOURCE 已含 CREATE TABLE/PROCEDURE 等然后用新用户登录:
sqlplus kaoqin_app/kaoqin123@localhost:1521/XE所有建表、建过程操作都在此用户下执行。这样即使脚本出错,删掉kaoqin_app用户就能彻底清理,不伤 Oracle 系统。
4.3 执行建表脚本:逐条验证外键依赖顺序
建表有严格顺序,必须先建被引用的父表,再建引用它的子表。按依赖关系排序:
faculty(院系)→major(专业)→classes(班级)→student(学生)faculty→teacher(任课教师)→classteacher(班主任)course(课程)qingjia(请假)→kaoqin_record(考勤)
避坑 / 常见问题 / 排查
现象:执行
CREATE TABLE student ...报错ORA-00942: table or view does not exist
原因:student表外键stu_class引用classes(class_no),但classes表还没创建。
解决:严格按上述顺序执行,或先把所有FOREIGN KEY约束注释掉,建完所有表后再用ALTER TABLE添加。现象:
CREATE TABLE qingjia ...报错ORA-00907: missing right parenthesis
原因:原文day_number nubmer not null拼写错误(nubmer应为number),且datetime类型在 Oracle 中不存在(应为DATE)。
解决:全文替换datetime→DATE,nubmer→number。Oracle 的DATE类型同时存日期和时间,精度秒级,够用。现象:插入学生数据时报
ORA-02291: integrity constraint violated - parent key not found
原因:student.stu_faculty外键指向faculty.faculty_id,但faculty表为空,或插入的faculty_id值不存在。
解决:先插入院系数据,例如INSERT INTO faculty VALUES (5, '软件学院');,再插学生时stu_faculty=5。现象:存储过程编译失败,提示
PLS-00302: component 'COURSE_NO' must be declared
原因:WHERE course_no = course_no中右侧course_no被解析为列名,非参数。
解决:按 3.1 节改用带p_前缀的参数名,避免同名列冲突。现象:视图
rjxy查询无结果,但kaoqin_record里有数据
原因:student.stu_faculty值不是5,或student表里stu_no与kaoqin_record.stu_number不匹配(如学号长度不一致:char(10)vsvarchar2(20))。
解决:SELECT stu_no, stu_faculty FROM student WHERE ROWNUM<10;查看实际值;用LENGTH(stu_no)检查长度。
4.4 插入测试数据:用最小集验证核心流程
不必填满所有表,用 5 条关键数据跑通闭环:
-- 1. 插入院系 INSERT INTO faculty VALUES (5, '软件学院'); -- 2. 插入专业 INSERT INTO major VALUES (101, '计算机科学与技术', 5); -- 3. 插入班级(班级号 'SJ08-1' 对应原文班级) INSERT INTO classes VALUES ('SJ08-1', '软件08-1班', 'CT001'); -- 4. 插入学生(学号 '0820980113' 对应原文学号) INSERT INTO student VALUES ('0820980113', '张三', '男', 'SJ08-1', 101, 5); -- 5. 插入课程 INSERT INTO course VALUES ('ORCL200901', 'Oracle数据库管理与应用', '必修'); -- 6. 插入一条考勤(模拟张三旷课) INSERT INTO kaoqin_record VALUES ( 'KQ20091229001', TO_DATE('2009-12-29 08:00:00', 'YYYY-MM-DD HH24:MI:SS'), '0820980113', '旷课', 'T001', 'ORCL200901' ); COMMIT;然后执行存储过程验证:
DECLARE v_cnt NUMBER; BEGIN getMessage('0820980113', 'ORCL200901', v_cnt); DBMS_OUTPUT.PUT_LINE('张三在Oracle课缺勤:' || v_cnt || '次'); END; / -- 输出:张三在Oracle课缺勤:1次5. 二次开发改造指南:从课程设计到可用系统的 3 个关键升级
5.1 权限体系升级:用角色(ROLE)替代硬编码视图
原文的rjxy视图只解决软件学院领导的权限,但系统有六类用户,每类权限不同。硬编码视图会爆炸式增长(rjxy,yyxy,wgy...)。正确做法是创建角色并授权:
-- 创建角色 CREATE ROLE role_student; CREATE ROLE role_teacher; CREATE ROLE role_headmaster; CREATE ROLE role_college_leader; CREATE ROLE role_school_leader; CREATE ROLE role_admin; -- 授权(以学生角色为例) GRANT SELECT ON student TO role_student; GRANT SELECT ON qingjia TO role_student WHERE stu_no = SYS_CONTEXT('USERENV','SESSION_USER'); -- 需配合VPD GRANT SELECT ON kaoqin_record TO role_student WHERE stu_number = SYS_CONTEXT('USERENV','SESSION_USER'); -- 创建学生用户并赋角色 CREATE USER stu_0820980113 IDENTIFIED BY pwd123; GRANT role_student TO stu_0820980113;注意:
WHERE子句在GRANT中不合法,此处仅为示意。真实实现需用 Oracle Virtual Private Database(VPD),创建策略函数f_student_policy返回stu_number = SYS_CONTEXT('USERENV','SESSION_USER'),再DBMS_RLS.ADD_POLICY绑定。虽然比视图复杂,但一套策略管所有表,维护成本断崖下降。
5.2 业务逻辑加固:请假审批流的状态机实现
原文qingjia表用char(10)存状态('0','1','2'),但缺乏状态变迁约束。应升级为状态机,用CHECK约束+触发器保证流程合规:
-- 修改表,增加状态字段 ALTER TABLE qingjia ADD ( status_code CHAR(2) DEFAULT '00' CHECK (status_code IN ('00','01','02','10','11','12')), status_desc VARCHAR2(20) ); -- 创建状态码映射表(便于前端展示) CREATE TABLE qingjia_status ( code CHAR(2) PRIMARY KEY, desc_cn VARCHAR2(20), next_codes VARCHAR2(50) -- 允许的下一个状态,如 '01,02' ); INSERT INTO qingjia_status VALUES ('00', '待班主任审批', '01,02'); INSERT INTO qingjia_status VALUES ('01', '班主任已同意', '10,11'); INSERT INTO qingjia_status VALUES ('02', '班主任已拒绝', '00'); -- ... 其他状态 COMMIT; -- 创建触发器,校验状态变迁合法性 CREATE OR REPLACE TRIGGER trg_qingjia_status_check BEFORE UPDATE ON qingjia FOR EACH ROW DECLARE v_allowed VARCHAR2(50); BEGIN SELECT next_codes INTO v_allowed FROM qingjia_status WHERE code = :old.status_code; IF NOT (','||v_allowed||',' LIKE '%,'||:new.status_code||',%') THEN RAISE_APPLICATION_ERROR(-20001, '非法状态变更:从 '||:old.status_code||' 到 '||:new.status_code); END IF; END;这样,当程序试图把状态从'00'(待审)直接改成'11'(院领导已拒),触发器立刻报错,从数据库层堵死逻辑漏洞。
5.3 性能与可观测性:为关键表添加索引与审计日志
考勤表kaoqin_record是高频查询表,必须建复合索引:
-- 按学生查课表:stu_number + course_no CREATE INDEX idx_kq_stu_course ON kaoqin_record(stu_number, course_no); -- 按课查考勤:course_no + sk_time(时间范围查询) CREATE INDEX idx_kq_course_time ON kaoqin_record(course_no, sk_time); -- 请假表按状态查:班主任审批流 CREATE INDEX idx_qj_status ON qingjia(class_tea_sp_status, coll_leader_sp_status);同时,开启 DML 审计,追踪谁在何时改了考勤:
-- 开启审计(需DBA权限) AUDIT INSERT, UPDATE, DELETE ON kaoqin_record BY ACCESS; -- 查看审计记录 SELECT timestamp, owner, obj_name, action_name, sessionid, username FROM dba_audit_trail WHERE owner = 'KAOQIN_APP' AND obj_name = 'KAOQIN_RECORD' ORDER BY timestamp DESC;审计日志能定位误操作(如老师手滑多录了一条),是生产环境的后悔药。
从那以后我每次接手一个课程设计类项目,都强制走一遍“建专用用户→跑最小数据集→查执行计划→开审计”的四步验证。不是为了炫技,而是因为太多人倒在第一步——在 SYSTEM 下建表,结果删库跑路时发现DROP USER都不让你删。这份 Oracle 学生考勤系统,表面是2009年的课设,内里是经得起推敲的工程骨架。它不追求花哨的 Web 界面,但把数据库该干的事干得扎实:关系清晰、约束到位、逻辑可溯、权限分明。希望帮到你。
本文还有配套的精品资源,点击获取