ARTICLE DETAIL

资讯详情

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

基于Oracle的停车场管理系统数据库课程设计:建表、存储过程与避坑全解析

基于Oracle的停车场管理系统数据库课程设计:建表、存储过程与避坑全解析 简介一套基于Oracle的停车场管理系统数据库课程设计源码与报告面向高校数据库课程设计学生涵盖需求分析、数据库设计概念结构、逻辑结构、序列/视图等、PL/SQL功能模块、实现与运维等完整环节。包体共6个文件包含两个SQL脚本建表查询及存储过程、两个Word文档设计报告正文与模板、一个Visio图形文件E-R模型等压缩后仅315KB。资源已有251人学习下载。内容以“系统需求分析—概念模型—逻辑结构—数据库实现—视图存储过程触发器”为主线提供八九个数据表、六七个存储过程及6个SQL案例并配有封面、目录、设计任务书、进度安排等报告框架便于对照修改和提交课程设计。1. 基于Oracle的停车场管理系统数据库课程设计这门课设计值得认真做的几个理由拿到「基于Oracle的停车场管理系统数据库课程设计源码报告」这个题目的人一开始大概率是高兴的停车场嘛表结构不复杂CRUD 写一遍就行。真正动手才发现同样的功能在 MySQL 里是十分钟的事在 Oracle 里却要面对序列、触发器、存储过程、字符集、服务名注册这一整套东西。反直觉的结论是这套课设计其实比看起来更有价值它逼着你去理解数据库端如何承载业务而不是把数据库当成一个会存数据的黑匣子。这篇笔记适合正在做数据库课程设计的在校生也适合刚从 Oracle 入门、想把 PL/SQL 和事务机制用起来的开发者。接下来的章节按建库、建模、写核心代码、灌数据、排错、答辩的顺序展开每一段都能直接照着操作。前端界面你用 Java Swing 还是 Python 都无所谓数据库这一侧的逻辑独立才是这个课设真正能立住的地方。2. 把停车场业务拆成关系模型五张核心表与字段设计2.1 停车场业务三大流程与数据流设计表结构之前先把停车场的一天拆成流程。入场环节要做三件事识别车牌判断是固定月租车还是临时车查找或登记车辆信息分配一个空闲车位并写入场记录。出场环节要做四件事根据车牌找入场记录按收费规则计算停车费写出场结算记录释放车位并把入场记录标记为已离场。此外还有场内管理比如统计当前占用率、按天汇总收入、查询某辆车的历史记录。这三个流程直接决定了数据模型的边界。入场要写车辆表和入场记录表出场要写出场记录表并更新车位状态场内管理需要按车位、按时间、按车辆类型做统计。如果你一开始就把所有字段塞进一张大表后面写统计 SQL 时会发现业务逻辑全糊在一起这在答辩现场非常容易被追问。所以先画三张流程草图再回头定义表是这门课设最稳妥的起点。2.2 核心建表脚本字段类型与约束怎么取舍我一般把核心表控制在五张车位表、车辆表、入场记录表、出场记录表、收费规则表再补一张操作员表作为辅助。入场和出场拆成两张表而不是一张合并表目的是让 ER 图上能体现一对多和一对一关系答辩时讲起来也有层次。-- 1. 车位表一个车位一条记录 CREATE TABLE PARKING_SPACE ( SPACE_ID NUMBER(8) NOT NULL, SPACE_CODE VARCHAR2(20) NOT NULL, AREA_NO VARCHAR2(10) NOT NULL, SPACE_TYPE VARCHAR2(10) DEFAULT 普通, STATUS VARCHAR2(10) DEFAULT 空闲, CREATE_TIME DATE DEFAULT SYSDATE, CONSTRAINT PK_PARKING_SPACE PRIMARY KEY (SPACE_ID), CONSTRAINT UK_SPACE_CODE UNIQUE (SPACE_CODE), CONSTRAINT CK_SPACE_TYPE CHECK (SPACE_TYPE IN (普通,固定,无障碍)), CONSTRAINT CK_SPACE_STATUS CHECK (STATUS IN (空闲,占用,禁用)) ); -- 2. 车辆表临时车第一次进场也要建档 CREATE TABLE VEHICLE ( VEHICLE_ID NUMBER(8) NOT NULL, PLATE_NO VARCHAR2(15) NOT NULL, VEHICLE_TYPE VARCHAR2(10) DEFAULT 小型车, OWNER_NAME VARCHAR2(20 CHAR), PHONE VARCHAR2(20), IS_MEMBER CHAR(1) DEFAULT N, CREATE_TIME DATE DEFAULT SYSDATE, CONSTRAINT PK_VEHICLE PRIMARY KEY (VEHICLE_ID), CONSTRAINT UK_PLATE_NO UNIQUE (PLATE_NO) ); -- 3. 入场记录表每辆车每次入场生成一条 CREATE TABLE ENTRY_RECORD ( RECORD_ID NUMBER(10) NOT NULL, VEHICLE_ID NUMBER(8) NOT NULL, SPACE_ID NUMBER(8) NOT NULL, ENTRY_TIME DATE NOT NULL, OPERATOR_ID VARCHAR2(10), STATUS VARCHAR2(10) DEFAULT 在场, CONSTRAINT PK_ENTRY PRIMARY KEY (RECORD_ID), CONSTRAINT FK_ENTRY_VEHICLE FOREIGN KEY (VEHICLE_ID) REFERENCES VEHICLE(VEHICLE_ID), CONSTRAINT FK_ENTRY_SPACE FOREIGN KEY (SPACE_ID) REFERENCES PARKING_SPACE(SPACE_ID), CONSTRAINT CK_ENTRY_STATUS CHECK (STATUS IN (在场,已离场)) ); CREATE INDEX IDX_ENTRY_TIME ON ENTRY_RECORD(ENTRY_TIME); -- 4. 出场结算表与入场记录一对一 CREATE TABLE EXIT_RECORD ( RECORD_ID NUMBER(10) NOT NULL, EXIT_TIME DATE NOT NULL, FEE_AMOUNT NUMBER(8,2) NOT NULL, PAY_TYPE VARCHAR2(10) DEFAULT 现金, OPERATOR_ID VARCHAR2(10), SETTLE_STATUS VARCHAR2(10) DEFAULT 已结算, CONSTRAINT PK_EXIT PRIMARY KEY (RECORD_ID), CONSTRAINT FK_EXIT_ENTRY FOREIGN KEY (RECORD_ID) REFERENCES ENTRY_RECORD(RECORD_ID) ); CREATE INDEX IDX_EXIT_TIME ON EXIT_RECORD(EXIT_TIME); -- 5. 收费规则表不同车型、不同车位类型对应不同价格 CREATE TABLE FEE_RULE ( RULE_ID NUMBER(4) NOT NULL, VEHICLE_TYPE VARCHAR2(10) NOT NULL, SPACE_TYPE VARCHAR2(10) NOT NULL, FREE_MINUTES NUMBER(3) DEFAULT 15, FEE_PER_HOUR NUMBER(6,2) DEFAULT 5, DAILY_CAP NUMBER(6,2) DEFAULT 30, RULE_STATUS VARCHAR2(6) DEFAULT 启用, CONSTRAINT PK_FEE_RULE PRIMARY KEY (RULE_ID) ); -- 辅助表操作员 CREATE TABLE OPERATOR ( OPERATOR_ID VARCHAR2(10) NOT NULL, USERNAME VARCHAR2(30) NOT NULL, PASSWORD VARCHAR2(64), ROLE VARCHAR2(20) DEFAULT 管理员, CONSTRAINT PK_OPERATOR PRIMARY KEY (OPERATOR_ID) );这段脚本里有几个值得留意的点。OWNER_NAME我写的是VARCHAR2(20 CHAR)而不是VARCHAR2(20)因为 Oracle 默认长度语义是 BYTE一个中文字符在 AL32UTF8 下占 3 字节20 BYTE 只能存 6 个汉字这是新手最容易翻车的地方。入场记录表和时间字段单独建了索引因为统计报表基本都按ENTRY_TIME和EXIT_TIME做条件没索引的话数据量一上来全表扫描会明显变慢。出场记录表的主键直接引用入场记录的主键表达“一次停车只有一条结算记录”这比单独再造一个自增主键更简洁。提示如果想让一辆车同一时刻只能占用一个车位可以建一条基于函数的唯一索引CREATE UNIQUE INDEX UX_ENTRY_ACTIVE ON ENTRY_RECORD(CASE WHEN STATUS在场 THEN VEHICLE_ID END)。课程设计阶段不要求但如果报告里写了这个逻辑记得在建表脚本里保留对应语句避免文档和代码对不上。2.3 数据字典与 ER 图报告里最容易被核对的两样东西课程设计报告里几乎必然要放数据字典和 ER 图而答辩老师最常做的事就是随机抽一张表的字段然后对照你提交的建表脚本来检查一致性。我见过不少报告字段名写的是中文含义代码里却是缩写现场直接被问住。最稳妥的做法是直接从建表脚本反向生成数据字典确保两者永远同步。以ENTRY_RECORD为例数据字典里至少要包含这些列字段类型约束业务含义RECORD_IDNUMBER(10)PK序列生成入场流水号VEHICLE_IDNUMBER(8)FK 到 VEHICLE车辆 IDSPACE_IDNUMBER(8)FK 到 PARKING_SPACE占用的车位ENTRY_TIMEDATENOT NULL入场时间STATUSVARCHAR2(10)CHECK 约束在场 / 已离场ER 图则按四条主线画VEHICLE一对多ENTRY_RECORDPARKING_SPACE一对多ENTRY_RECORDENTRY_RECORD一对一EXIT_RECORDFEE_RULE按车型和车位类型与出场记录做匹配。收费规则不直接用外键关联而是通过业务字段匹配这样调价时不需要改动历史记录这个设计在报告的“规则设计”部分要写清楚。3. 用 Oracle 原生能力实现核心功能序列、触发器与存储过程3.1 为什么要把业务逻辑放进数据库而不是 Java 程序课程设计如果只是把停车费计算写在 Java 里数据库端就只剩增删改查等于把 Oracle 用成了 MySQL。答辩时老师问“你这个系统如果同时有十个窗口在结算怎么保证费用一致”只靠应用层很难回答得干净。把核心结算逻辑写进存储过程数据库用事务保证“写结算记录、释放车位、更新入场状态”要么全部成功要么全部回滚这个答案就顺理成章了。这不是说所有逻辑都要进数据库。像界面渲染、导出报表格式这些与应用强相关的东西留在前端没问题但涉及数据一致性、审计和统计的部分放在 Oracle 里更站得住脚。所以这个章节的代码都围绕序列、触发器、存储过程展开这是 Oracle 数据库课程设计与 MySQL 课设拉开差距的地方。3.2 序列加触发器让 Oracle 拥有自增主键Oracle 没有 MySQL 那种列级 AUTO_INCREMENT常见做法是用序列生成主键再用触发器自动填值。车辆表和入场记录表各建一个序列。-- 车辆表主键序列从 1 开始每次加 1 CREATE SEQUENCE VEHICLE_SEQ START WITH 1 INCREMENT BY 1 NOCACHE; -- 入场记录主键序列 CREATE SEQUENCE ENTRY_SEQ START WITH 1 INCREMENT BY 1 NOCACHE; -- 车辆表主键触发器插入前如果没传 ID就自动从序列取值 CREATE OR REPLACE TRIGGER TRG_VEHICLE_ID BEFORE INSERT ON VEHICLE FOR EACH ROW WHEN (NEW.VEHICLE_ID IS NULL) BEGIN SELECT VEHICLE_SEQ.NEXTVAL INTO :NEW.VEHICLE_ID FROM DUAL; END; / -- 入场记录触发器 CREATE OR REPLACE TRIGGER TRG_ENTRY_ID BEFORE INSERT ON ENTRY_RECORD FOR EACH ROW WHEN (NEW.RECORD_ID IS NULL) BEGIN SELECT ENTRY_SEQ.NEXTVAL INTO :NEW.RECORD_ID FROM DUAL; END; /这段代码的核心是:NEW伪记录它代表即将插入的新行。WHEN (NEW.RECORD_ID IS NULL)这个条件很重要意思是只有你没手动指定主键时才去取序列这样初始化脚本里万一有INSERT ... VALUES (1000, ...)也不会把序列打乱。NOCACHE在课设规模下没问题生产环境为了减少序列争用会改成CACHE 20但缓存会让序列号出现跳号属于正常现象不要误认为是故障。3.3 收费结算存储过程TRUNC、CEIL 与事务的配合出场结算是全系统最核心的一段逻辑。入场记录表里保存了入场时间收费规则表里保存了免费分钟数、每小时的费用和单日封顶值。存储过程要做的事就是把这两处数据查出来算出金额再在同一个事务里写出场记录、释放车位、更新入场状态。CREATE OR REPLACE PROCEDURE P_EXIT_SETTLE ( P_RECORD_ID IN NUMBER, P_PAY_TYPE IN VARCHAR2 DEFAULT 现金, P_OPERATOR_ID IN VARCHAR2 DEFAULT U001, P_FEE OUT NUMBER ) AS V_ENTRY_TIME DATE; V_VEHICLE_TYPE VARCHAR2(20); V_SPACE_TYPE VARCHAR2(10); V_FREE_MINUTES NUMBER(3); V_FEE_PER_HOUR NUMBER(6,2); V_DAILY_CAP NUMBER(6,2); V_MINUTES NUMBER; BEGIN -- 1. 取入场时间、车辆类型和车位类型 SELECT E.ENTRY_TIME, V.VEHICLE_TYPE, S.SPACE_TYPE INTO V_ENTRY_TIME, V_VEHICLE_TYPE, V_SPACE_TYPE FROM ENTRY_RECORD E JOIN VEHICLE V ON V.VEHICLE_ID E.VEHICLE_ID JOIN PARKING_SPACE S ON S.SPACE_ID E.SPACE_ID WHERE E.RECORD_ID P_RECORD_ID AND E.STATUS 在场; -- 2. 按车型和车位类型匹配收费规则 SELECT FREE_MINUTES, FEE_PER_HOUR, DAILY_CAP INTO V_FREE_MINUTES, V_FEE_PER_HOUR, V_DAILY_CAP FROM FEE_RULE WHERE VEHICLE_TYPE V_VEHICLE_TYPE AND SPACE_TYPE V_SPACE_TYPE AND RULE_STATUS 启用; -- 3. 计算停车分钟数DATE 相减得到天数乘 1440 换算成分钟 V_MINUTES : ROUND((SYSDATE - V_ENTRY_TIME) * 24 * 60); -- 4. 免费时段内不收钱超过部分按小时向上取整 IF V_MINUTES V_FREE_MINUTES THEN P_FEE : 0; ELSE P_FEE : CEIL((V_MINUTES - V_FREE_MINUTES) / 60) * V_FEE_PER_HOUR; IF P_FEE V_DAILY_CAP THEN P_FEE : V_DAILY_CAP; END IF; END IF; -- 5. 写结算记录、释放车位、更新入场状态全部在一个事务里 INSERT INTO EXIT_RECORD (RECORD_ID, EXIT_TIME, FEE_AMOUNT, PAY_TYPE, OPERATOR_ID) VALUES (P_RECORD_ID, SYSDATE, P_FEE, P_PAY_TYPE, P_OPERATOR_ID); UPDATE PARKING_SPACE S SET S.STATUS 空闲 WHERE S.SPACE_ID (SELECT E.SPACE_ID FROM ENTRY_RECORD E WHERE E.RECORD_ID P_RECORD_ID); UPDATE ENTRY_RECORD E SET E.STATUS 已离场 WHERE E.RECORD_ID P_RECORD_ID; COMMIT; EXCEPTION WHEN NO_DATA_FOUND THEN ROLLBACK; RAISE_APPLICATION_ERROR(-20001, 入场记录不存在或收费规则未配置); WHEN OTHERS THEN ROLLBACK; RAISE; END P_EXIT_SETTLE; /这个过程的参数有四个P_RECORD_ID是入场记录的主键P_PAY_TYPE是支付方式默认现金P_OPERATOR_ID是当前操作员P_FEE是 OUT 参数用于把计算出的金额返回给调用方。第 4 步里CEIL的语义是向上取整停车 61 分钟按 2 小时计费这需要和收费规则保持一致写报告时要说明。TRUNC(SYSDATE)在这个过程里没有出现但统计当日入场次数时它非常常用-- 统计今天从 0 点到现在一共有多少辆车入场 SELECT COUNT(*) AS TODAY_ENTRY_COUNT FROM ENTRY_RECORD WHERE TRUNC(ENTRY_TIME) TRUNC(SYSDATE);TRUNC会把时间截断到当天零点这样对比两边的日期部分即可不需要关心时分秒。这是 Oracle 里处理按天统计的高频写法建议熟练掌握。调用存储过程时用命名参数传递可读性最好DECLARE V_FEE NUMBER; BEGIN P_EXIT_SETTLE( P_RECORD_ID 1, P_PAY_TYPE 微信, P_OPERATOR_ID U001, P_FEE V_FEE ); DBMS_OUTPUT.PUT_LINE(本次应收 || V_FEE || 元); END; /3.4 Oracle 分页与统计视图报表怎么写才像样分页是 Oracle 和 MySQL 差异最大的地方。MySQL 写LIMIT 0, 20Oracle 11g 及更早版本要借助ROWNUM分两层嵌套SELECT * FROM ( SELECT T.*, ROWNUM AS RN FROM (SELECT * FROM EXIT_RECORD ORDER BY EXIT_TIME DESC) T WHERE ROWNUM :P_PAGE_END ) WHERE RN :P_PAGE_START;这里内层先按出场时间排序中层取前 N 条并生成行号外层再过滤掉前 M 条。三层顺序不能乱否则会出现分页数据重复或丢失。如果数据库是 12c 或 19c直接用新的OFFSET FETCH语法更省事SELECT * FROM EXIT_RECORD ORDER BY EXIT_TIME DESC OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY;统计类报表建议建成视图。比如按天汇总收入CREATE OR REPLACE VIEW V_DAILY_REPORT AS SELECT TRUNC(EXIT_TIME) AS STAT_DATE, COUNT(*) AS TOTAL_ORDERS, SUM(FEE_AMOUNT) AS TOTAL_FEE, ROUND(AVG(FEE_AMOUNT), 2) AS AVG_FEE FROM EXIT_RECORD GROUP BY TRUNC(EXIT_TIME);视图的好处是报告里可以直接引用它写测试用例老师看到SELECT * FROM V_DAILY_REPORT就能明白你已经把报表需求固化到了数据库端。4. 从建库到跑通数据用户表空间、字符集与初始化流水4.1 19c 的 CDB/PDB为什么你连不上刚建好的库Oracle 12c 之后引入了容器数据库概念一个实例下可以有多个 PDB。很多课设环境装的是 19c默认安装可能只创建了一个名为ORCLPDB1的可插拔数据库。你如果拿system用户登录 CDB然后直接建表会发现后面用PARKING_USER登录时根本看不到这些表因为对象建在了不同容器里。先确认当前连接的是哪个容器SHOW CON_NAME; SELECT NAME FROM V$PDBS;如果输出显示你在CDB$ROOT而业务用户要建在ORCLPDB1下一步就需要切换容器。如果 SQL Developer 连接时就填了 PDB 的 Service Name可以省略切换这一步。4.2 创建表空间、用户与授权ALTER SESSION SET CONTAINER ORCLPDB1; -- 为停车场系统单独划一个表空间 CREATE TABLESPACE PARKING_DB DATAFILE /u01/app/oracle/oradata/ORCLPDB1/parking_db01.dbf SIZE 200M AUTOEXTEND ON NEXT 50M MAXSIZE 2G; -- 业务用户默认表空间指向 PARKING_DB CREATE USER PARKING_USER IDENTIFIED BY parking123 DEFAULT TABLESPACE PARKING_DB QUOTA UNLIMITED ON PARKING_DB TEMPORARY TABLESPACE TEMP; -- 课设阶段 CONNECT RESOURCE 两个角色基本够用 GRANT CONNECT, RESOURCE TO PARKING_USER;注意DATAFILE路径要改成你机器上 Oracle 实际的数据文件目录Linux 下通常是/u01/app/oracle/oradata/加 PDB 名Windows 下则是盘符路径。RESOURCE角色包含建表、建过程等权限但不包含无限表空间配额所以QUOTA UNLIMITED必须单独给。实际操作中如果密码复杂度策略开启parking123这种简单密码可能不被接受报错时换成Parking#2024这类带大写和特殊字符的密码即可。连接测试用这条命令sqlplus parking_user/parking123localhost:1521/ORCLPDB1如果提示ORA-12514先别怀疑密码多半是服务名写错了具体排查方法在第 5 章展开。4.3 SQL Developer 连接与字符集校准SQL Developer 连接时Connection Type 选 Basic填主机、端口、Service Name。Service Name 填ORCLPDB1或ORCL要看监听器实际注册的名字用lsnrctl services能查出来。连接成功后先做一次字符集体检SELECT * FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER NLS_CHARACTERSET; SELECT USERENV(LANGUAGE) FROM DUAL;第一条查数据库字符集第二条查当前会话语言环境。如果数据库是AL32UTF8而客户端NLS_LANG没设置SQL*Plus 里插入中文就可能乱码。Linux 下可以在环境变量里指定export NLS_LANGAMERICAN_AMERICA.AL32UTF8SQL Developer 不读取这个环境变量它有独立的 NLS 设置界面里默认通常没问题但用 SQL*Plus 跑脚本前一定要确认。4.4 初始化测试数据一个月流水让报表不空转课设报告里的统计查询最怕空表跑出来全是 0答辩很难看。建议预置 40 个车位、30 辆测试车辆和一个月左右的门禁流水。-- 预置 40 个普通车位 DECLARE BEGIN FOR A IN 1..2 LOOP FOR B IN 1..20 LOOP INSERT INTO PARKING_SPACE (SPACE_ID, SPACE_CODE, AREA_NO, SPACE_TYPE) VALUES ((A - 1) * 20 B, A || - || LPAD(B, 2, 0), A区, 普通); END LOOP; END LOOP; COMMIT; END; / -- 预置 30 辆测试车辆 DECLARE BEGIN FOR I IN 1..30 LOOP INSERT INTO VEHICLE (PLATE_NO, VEHICLE_TYPE, OWNER_NAME) VALUES (京A || LPAD(I, 5, 0), 小型车, 测试车主 || I); END LOOP; COMMIT; END; / -- 生成 6 月入场流水每天 40 笔间隔 15 分钟车辆循环复用 DECLARE V_BASE_TIME DATE : TO_DATE(2025-06-01 07:30:00, YYYY-MM-DD HH24:MI:SS); BEGIN FOR D IN 0..29 LOOP FOR I IN 1..40 LOOP INSERT INTO ENTRY_RECORD (VEHICLE_ID, SPACE_ID, ENTRY_TIME, OPERATOR_ID) VALUES (MOD(I, 30) 1, MOD(I, 40) 1, V_BASE_TIME D (I - 1) * 15 / 1440, U001); END LOOP; END LOOP; COMMIT; END; /这段脚本的可复现性很好车位编码按区域和序号生成车辆轮流进出每天 40 笔分布在 07:30 到 17:15 之间。V_BASE_TIME D表示加 D 天(I - 1) * 15 / 1440表示每 15 分钟一辆。跑完后可以用第 3.4 节的视图验证6 月应该每天都有 40 条记录这就是后面报表的素材。5. Oracle 课设常见问题排查现象、原因与解法5.1 ORA-12514监听器不认识服务名现象SQL*Plus 或 SQL Developer 连接时报ORA-12514: TNS:listener does not currently know of service requested in connect descriptor但数据库服务明明在运行。原因连接串里的 Service Name 和监听器实际注册的名字不一致。12c 以后默认监听可能注册了多个服务比如ORCL、ORCLPDB1、XDB你写的那个名字恰好不在其中。解决在数据库服务器上执行lsnrctl services看输出里的 Service 名称。然后在 tnsnames 或连接工具里填这个实际存在的 Service Name而不是凭记忆猜。如果确认名字正确还报错执行ALTER SYSTEM REGISTER;手动触发监听注册再试一次。5.2 ORA-00942表或视图不存在但明明建过表现象用PARKING_USER登录后执行SELECT * FROM ENTRY_RECORD报ORA-00942: table or view does not exist用SYSTEM用户却能看到这张表。原因对象属于别的用户当前用户没有访问权限或者没有加同义词。另一个常见情况是脚本执行顺序错了先跑了报表脚后建的表。解决先确认对象归属SELECT OWNER, TABLE_NAME FROM ALL_TABLES WHERE TABLE_NAME ENTRY_RECORD;如果表确实属于PARKING_USER要给另一个用户授权并建同义词GRANT SELECT, INSERT, UPDATE ON PARKING_USER.ENTRY_RECORD TO REPORT_USER; CREATE SYNONYM REPORT_USER.ENTRY_RECORD FOR PARKING_USER.ENTRY_RECORD;注意 Oracle 默认把对象名转成大写查询条件里写小写表名会找不到。5.3 中文乱码数据的后悔药怎么吃现象插入的中文显示成??或者 SQL Developer 里正常、SQL*Plus 里乱码。原因客户端字符集和数据库字符集不一致。数据库是ZHS16GBK而客户端用了AL32UTF8或者反过来都会出问题。解决先确认数据库字符集SELECT VALUE FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER NLS_CHARACTERSET;再按结果设置客户端。Linux 下用export NLS_LANGAMERICAN_AMERICA.AL32UTF8Windows 下在注册表里改NLS_LANG。如果数据已经写入乱码最直接的后悔药是UPDATE VEHICLE SET OWNER_NAME 测试车主 WHERE PLATE_NO 京A00001;重新执行脚本前先TRUNCATE TABLE清理数据避免重复插入报主键冲突。5.4 ORA-12899中文存不进 VARCHAR2现象INSERT INTO VEHICLE (PLATE_NO, OWNER_NAME) VALUES (京A00001, 张三丰)报ORA-12899: value too large for column。原因Oracle 的VARCHAR2(20)默认是 20 字节不是 20 个字符。UTF8 下一个汉字占 3 字节20 字节最多存 6 个汉字。解决建表时明确写VARCHAR2(20 CHAR)。已经建好的表可以调整ALTER TABLE VEHICLE MODIFY (OWNER_NAME VARCHAR2(30 CHAR));如果不想逐列改可以在建表前执行ALTER SESSION SET NLS_LENGTH_SEMANTICSCHAR;但这只影响当前会话之后创建的字段。5.5 ORA-00001序列和触发器不同步导致主键冲突现象手动执行了几条插入语句指定了主键值后来再用触发器自动生成 ID 时报ORA-00001: unique constraint violated。原因序列的当前值与表里已有的最大主键值不一致。序列只会递增不会自动跳到已有最大值。解决把序列同步到当前最大 ID。常见做法是ALTER SEQUENCE ENTRY_SEQ INCREMENT BY 1000; SELECT ENTRY_SEQ.NEXTVAL FROM DUAL; ALTER SEQUENCE ENTRY_SEQ INCREMENT BY 1;这里的 1000 是经验值只要让序列跳到一个明显大于当前最大 ID 的位置即可。更严谨的方式是先查MAX(RECORD_ID)再按差值调整。6. 让报告的验收更顺利验证脚本与答辩讲解技巧源码和报告要能互相印证最省力的办法是准备一份验证脚本在答辩前把关键数据跑一遍。我常用的自检 SQL 是这样的SELECT 车位 AS 检查项, COUNT(*) AS 行数 FROM PARKING_SPACE UNION ALL SELECT 车辆, COUNT(*) FROM VEHICLE UNION ALL SELECT 入场记录, COUNT(*) FROM ENTRY_RECORD UNION ALL SELECT 出场记录, COUNT(*) FROM EXIT_RECORD; -- 按天核对停车收入 SELECT TRUNC(ENTRY_TIME) AS 停车日期, COUNT(*) AS 入场笔数, COUNT(DISTINCT VEHICLE_ID) AS 车辆数 FROM ENTRY_RECORD GROUP BY TRUNC(ENTRY_TIME) ORDER BY 停车日期;第一条查各表行数确认初始化数据完整第二条按天分组确认流水分布正常。报告里的测试用例不要另写一套直接复用这些 SQL把结果截图放进去。答辩讲到存储过程时重点讲三件事为什么用CEIL向上取整为什么把插入、更新放在一个事务里以及异常时如何回滚。讲到触发器时说明WHEN (NEW.RECORD_ID IS NULL)这个条件避免了手动指定主键对序列的干扰。我第一次用 Oracle 做课设时把建表脚本、数据字典和测试记录分成三份文件维护后来改了某个字段长度忘了同步答辩现场被老师翻出来。现在我的习惯是代码改完先跑一遍验证脚本再用脚本生成数据字典保证报告和源码永远来自同一份文件。课设做到这个程度老师想挑毛病都难。希望帮到你。本文还有配套的精品资源点击获取
返回列表