☰
openGauss数据库九实验实战:从建库建表到JDBC与备份恢复
2026/10/27 3:11:51 网站建设 项目流程

简介:这份zzu数据库实验报告书面向计算机科学与技术、软件工程等专业学生及数据库初学者,围绕《数据库系统原理实验》课程提供从理论到实践的完整记录,可用于实验预习、过程对照、报告撰写参考与期末复盘。压缩包内共1个docx文件,约3.23MB,按实验一至实验九组织,涵盖认识DBMS系统、创建数据库表与索引、交互式SQL、视图、完整性控制、安全性控制、事务与并发控制、备份恢复及JDBC连接数据库等模块,并保留实验目的、内容、问题更正与结果分析等材料。已有1525人学习,说明其在课程实验场景中具有一定参考面。读者可据此梳理openGauss环境下的基本操作脉络,理解SQL查询、权限分配、事务一致性、锁与死锁处理、数据恢复和JDBC编程等关键知识点,也能借鉴实验报告的记录方式与排版结构,适合作为数据库课程实验的辅助材料。

1. 从九个实验串起的数据库实战线:这份报告书能解决什么

很多人学完数据库课程,范式背得滚瓜烂熟,考试能画 ER 图,可真让他开一台机器、装一个实例、连上去把六张业务表建起来、加上外键、配好权限、再跑一遍事务回滚,手就停在第一条命令上了。这份《数据库系统原理实验》报告书正好卡在这个断层上:九个实验从认识 DBMS 一路排到用 JDBC 连库,每一步都有命令、有截图位、有结果分析,把数据库从概念拉回到命令行。它面向的是计算机类、软件工程专业正在上实验课的学生,也适合已经工作、想系统补一遍数据库操作的开发者。当成一份带顺序的实操清单来用,照着敲一遍,比翻十篇零散教程实在。

2. 报告骨架与环境落地:九次实验怎么排、openGauss 怎么连

2.1 九个实验的递进关系:一条从「认识工具」到「写代码连库」的主线

这份报告价值最高的地方不是单个实验写得多细,而是九个实验之间的依赖关系排得很清楚。实验一是把环境生出来,实验二在这台环境上建库建表,实验三到实验八全部是在同一个db_school库上做查询、视图、约束、权限、事务、备份,实验九再用 JDBC 从外部程序回去访问它。换句话说,它是一套连续迭代的项目,不是九个互不相干的练习。

实验主题主要产物前置依赖
实验一认识 DBMS 系统实例启动、gsql 可连接、新用户建好无
实验二创建数据库、表和索引表空间、库、模式、六张表、索引实验一
实验三交互式 SQL 语句查询、插入、更新、删除语句集实验二
实验四创建视图视图定义与基于视图的查询实验二、三
实验五完整性控制主键、外键、CHECK、触发器实验二
实验六安全性控制用户、角色、GRANT/REVOKE实验一
实验七事务与并发控制事务边界、隔离级别、锁观察实验二
实验八备份与恢复逻辑备份文件、恢复验证实验二
实验九使用 JDBC 连接数据库可运行的 Java 访问样例全部

这个顺序意味着你不能跳着做。实验五要给SC表加成绩范围约束,前提是SC表已经在实验二存在;实验八要备份db_school,前提是里面有数据。我见过有人直接从实验七开始,结果连事务要操作的表都没有,只能临时瞎建两张表,最后报告写得很热闹,其实什么都没验证。按表里这一列「前置依赖」走,是最省时间的路径。

环境这一层,报告给了三种安装模式:云上装、虚拟机一键装、先装 openEuler 再手动装数据库。三者的取舍很直白——只想把实验做完、不想折腾操作系统,选一键装;想顺便把 Linux 用户、目录权限、服务管理一起练了,选手动装。报告本身偏向手动装,因为后面大量的su - omm、gs_om、\d+操作都是围绕着一个本地实例展开的。

2.2 从虚拟机到 gsql:三条命令把实例跑起来

报告里最容易被跳过、也最容易翻车的部分,是「切换用户环境」这一步。数据库服务是以安装用户身份运行的,你直接用 root 敲gsql,大概率得到的是 command not found 或者认证失败。标准起手式是这样:

# 1. 切到数据库安装用户;注意 - 后面有空格,走的是登录 shell # 这样才会加载 ~/.bashrc 里的 GAUSSHOME、LD_LIBRARY_PATH 等变量 su - omm # 2. 启动实例;-t 后面跟动作,start/stop/status/restart gs_om -t start # 3. 连接默认库 postgres # -d 指定库名,-p 指定端口,-r 开启回显(只对交互式调试有用) gsql -d postgres -p 26000 -r

三个参数值得单独说清楚。-d postgres里的postgres是安装完成后自动生成的默认库,它存在的意义是给你一个「落脚点」,让你能连上去执行CREATE DATABASE建自己的库——你不能在没有连上任何库的情况下建库。-p 26000是主节点默认端口,很多人照着网上别的数据库教程写成 5432,然后卡在连接超时上。-r只在手工敲语句时有用,它把执行过的语句回显出来,写实验报告截图时字体清晰一些。

第一次连上之后,报告要求做的事情是创建数据库用户。这里有个辨析值得记住:默认只有安装时创建的管理员用户能访问初始库,你是不能拿管理员账号一路做到底的。建一个新用户、用它去连、用它去建表,才是完整的「DBMS 使用」体验。命令形态大致是CREATE USER u_student IDENTIFIED BY '...',密码要满足复杂度策略,太简单的会直接被拒。这一步看着琐碎,其实它和实验六的安全性控制是同一条线的开头——你在实验一随手建的这个用户,实验六里就是被 GRANT/REVOKE 操作的对象。

如果gs_om -t start报错,别急着重装。先gs_om -t status --detail看实例状态和日志路径,十有八九是磁盘空间、目录权限或者上一次没正常关闭留下的残留进程。这类问题在虚拟机环境里极其常见,报告里没写,但它是真实耗时最多的一环。

2.3 gsql 元命令清单:不写 SQL 也能把库看明白

实验一里有一大段元命令(反斜杠开头的命令)的整理,这部分内容在后面的实验里会被反复用到,值得单独抄成一张小抄贴在旁边。

元命令作用等价 SQL
\?查看所有元命令帮助无
\l列出所有数据库select datname from pg_database;
\c dbname切换数据库无
\c - username切换用户无
\dt列出当前库的表查pg_tables
\d+ tablename查看表结构、存储、分布信息无直接等价
\d tablename只看表结构无
\dn列出所有模式查pg_namespace
\di列出索引查pg_indexes
\db列出表空间select spcname from pg_tablespace;
\h查看 SQL 语法帮助无
\q退出无

\d+和\d的区别很实用:\d给你列名、类型、是否非空,够写报告;\d+还会带上存储参数、压缩方式、所属表空间这些,排查「表建到哪个表空间去了」的时候只能靠它。\di用来验证「主键是不是自动建了索引」这个结论——建完Students表直接敲\di,你会看到主键上确实挂了一个索引,这是 DBMS 自动完成的,不需要你手写CREATE INDEX。

报告里还整理了三组概念辨析:模式(Schema)与库、用户与角色、索引与目录。前两个在后面章节会直接用上,这里先把结论落地:模式是库内部的一层命名空间,一个库可以有多个模式,用来把不同业务的对象隔开;用户是带登录能力的账户,角色是权限的集合,把角色授予用户,用户就继承了这批权限。这两条理解了,实验二和实验六就不会混。

3. 建库建表建索引:从表空间到外键约束的完整链路

3.1 表空间、数据库、模式:三层容器的正确打开顺序

初学者最容易在这里乱序:直接CREATE DATABASE,然后发现所有东西都堆在默认目录里,想挪挪不动。报告里给的顺序是「先有目录,再有表空间,再有库,最后在库里建模式」,这个顺序不能反。

-- 1. 表空间:先准备一个空目录(必须是空目录),再注册进数据库 -- RELATIVE LOCATION 是相对数据目录的路径,换机器部署时不用改绝对路径 CREATE TABLESPACE tpcds_local RELATIVE LOCATION 'tablespace/tablespace_1'; -- 2. 建库时指定默认表空间;不指定就落到 pg_default CREATE DATABASE db_sc WITH TABLESPACE = tpcds_local; -- 3. 改名 ALTER DATABASE db_sc RENAME TO db_school;

RELATIVE LOCATION后面的路径是相对于实例数据目录的,不是相对于你当前所在的目录,这一点报告里没强调,但它是「目录建好了却报错找不到」的头号原因。目录必须事先建好且为空,数据库不会帮你创建。建完库之后,要\c db_school切过去,后面的表才建在这个库里——不切库直接建表,表会落在postgres库里,这是新手最常见的「我明明建了表,\dt却看不到」。

模式的用法是这样的:

-- 在当前库里建一个命名空间 CREATE SCHEMA book_eg; -- 建表时带上模式前缀,对象就归属这个模式 CREATE TABLE book_eg.teachers ( tno CHAR(20) PRIMARY KEY, tname CHAR(20) NOT NULL ); -- 级联删除:模式下的对象一起删掉,不加 CASCADE 会因为有对象而拒绝删除 DROP SCHEMA book_eg CASCADE;

模式的意义在于隔离。同一个库里,你完全可以让两个模式各有一张叫teachers的表,互不冲突,权限也可以按模式粒度授予。如果不写模式前缀,对象默认落到public模式,这也是为什么大多数人感觉不到模式的存在——它一直在,只是被默认值藏起来了。

3.2 六张核心表:列级约束和表级约束怎么分

实验二要建的是教学管理场景的六张表:教师、院系、学生、课程、选课、授课。建表顺序和外键方向是绑死的,不能随便排。

-- 教师表:主键、非空、CHECK 都写在列定义上,属于列级约束 CREATE TABLE Teachers ( Tno CHAR(20) PRIMARY KEY, Tname CHAR(20) NOT NULL, Sex CHAR(20) CHECK (Sex IN ('男','女')), Birthday DATE, Title CHAR(20), Dno CHAR(20) ); -- 院系表:负责人外键指向教师表 CREATE TABLE Departments ( Dno CHAR(20) PRIMARY KEY, Dname CHAR(20), Dheadno CHAR(20), FOREIGN KEY (Dheadno) REFERENCES Teachers(Tno) ); -- 学生表:专业编号外键指向院系 CREATE TABLE Students ( Sno CHAR(20) PRIMARY KEY, Sname CHAR(20) NOT NULL, Sex CHAR(20) CHECK (Sex IN ('男','女')), Birthday DATE, Enrollyear CHAR(20), Speciality CHAR(20), Dno CHAR(20), FOREIGN KEY (Dno) REFERENCES Departments(Dno) ); -- 课程表:学时、学分用 SMALLINT 就够,没必要上 INT CREATE TABLE Courses ( Cno CHAR(20) PRIMARY KEY, Cname CHAR(20) NOT NULL, Period SMALLINT, Credit SMALLINT ); -- 选课表:组合主键,成绩单独加范围检查 CREATE TABLE SC ( Sno CHAR(20), Cno CHAR(20), Grade SMALLINT CHECK (Grade BETWEEN 0 AND 100), PRIMARY KEY (Sno, Cno), FOREIGN KEY (Sno) REFERENCES Students(Sno), FOREIGN KEY (Cno) REFERENCES Courses(Cno) );

这里有两处设计要点,报告里的辨析其实就是在解释它们。第一处是Teachers.Dno和Departments.Dheadno互相引用,形成循环依赖。你不可能同时建这两张表还都带外键,正确做法是先建表、后补约束:

-- 循环引用的破法:先建表不带外键,事后 ALTER TABLE 补上 -- CONSTRAINT 后面起名字,方便以后单独 DROP CONSTRAINT 而不影响整张表 ALTER TABLE Teachers ADD CONSTRAINT fk_teachers_dept FOREIGN KEY (Dno) REFERENCES Departments(Dno);

第二处是列级约束和表级约束的分界。规则很干脆:涉及多个列的约束,必须写成表级;只涉及单列的,写在列后面(列级)或者写在一起(表级)都行。SC的组合主键PRIMARY KEY (Sno, Cno)只能是表级,因为它同时约束两列;Grade BETWEEN 0 AND 100只碰一列,放列后面即可。至于\d+ Teachers查出来的东西看不到约束名,而表级约束能起名,这也是后续排查时区分两者的一个抓手——报错信息里带着约束名,定位就快得多。

还有一点报告里提到但我建议提前记住:CHAR(20)是定长,不足 20 位会在右侧补空格。外键两端的列类型和长度必须能对得上,CHAR(20)去引用VARCHAR(20)在很多情况下会直接报「没有唯一约束匹配」的错,明明被引用列确实是主键。这个坑在避坑章还会再出现一次。

3.3 索引:主键自动建,手建的要验证

实验二的最后一步是建索引和删索引。主键索引是 DBMS 自动建的,\di能看到;手建索引是用在那些高频过滤但又不是主键的列上,比如学生表的院系编号。

-- 学生按院系查询是高频操作,给 Dno 建索引 CREATE INDEX student_dept ON Students(Dno); -- 查看当前库所有索引,确认索引确实建上了 \di -- 验证索引是否被使用:看执行计划里是 Seq Scan 还是 Index Scan EXPLAIN SELECT * FROM Students WHERE Dno = 'D01'; -- 删索引:索引名可能和表名重复,注意别删错 DROP INDEX student_dept;

EXPLAIN这一步是报告里没写死、但我建议每次都做的。刚建完表、只插了三五条测试数据的情况下,优化器算了一下成本,认为全表扫描比走索引还便宜,照样输出Seq Scan。这不是索引没建成功,是数据量不够。写报告时如果要在截图里证明索引生效,得先灌进去几百上千行数据,再执行一次ANALYZE更新统计信息,执行计划才会翻过来。

4. SQL、视图与四类控制:把查询、权限、事务、备份落到语句上

4.1 交互式 SQL 与视图:把复杂查询封成一张表

实验三和实验四是一组。实验三练的是增删改查的基本功,实验四在此基础上把复杂查询固化成视图,让调用方不用每次重写一遍多表连接。

-- 基础查询:按入学年份过滤 SELECT Sno, Sname, Speciality FROM Students WHERE Enrollyear = '2021' ORDER BY Sno; -- 聚合查询:每门课的平均分与选课人数 SELECT Cno, AVG(Grade) AS avg_grade, COUNT(*) AS cnt FROM SC GROUP BY Cno HAVING COUNT(*) >= 2; -- 视图:把「各院系学生人数」这件事封起来 CREATE VIEW v_dept_cnt AS SELECT Dno, COUNT(*) AS cnt FROM Students GROUP BY Dno; -- 基于视图再查,语句短了很多 SELECT * FROM v_dept_cnt WHERE cnt > 30;

视图这层封装的价值在于「口径统一」。v_dept_cnt里人数怎么算、算不算空院系,规则只写在视图定义里一处,十个调用方都引用它,不会出现各写各的、结果对不上的情况。但要清楚它的边界:视图本身不存数据,它每次执行都回到底层表去算,所以「建了视图查询就变快」是错觉,真正提速靠的是索引和物化。

另一个细节是可更新视图。简单的单表投影视图通常可以INSERT/UPDATE,跨表连接、带聚合、带DISTINCT的视图基本只读。如果希望「通过视图插入的数据必须还能被这个视图查出来」,要加WITH CHECK OPTION,否则你插进去一条不满足视图WHERE的行,插成功了却查不到,这种自相矛盾在联调时极其难查。

4.2 完整性控制与安全性控制:约束管数据,权限管人

实验五和实验六是两条正交的线:完整性约束的是「数据本身对不对」,安全性控制的是「谁能碰这些数据」。前者靠主键、外键、NOT NULL、CHECK这类声明式约束实现,声明式搞不定的场景才上触发器。

-- 触发器:成绩范围用 CHECK 能兜住,但如果要做的是跨表联动校验 -- 就得用触发器。下面这个在写入前拦一道,超范围直接抛异常 CREATE OR REPLACE FUNCTION check_grade() RETURNS TRIGGER AS $$ BEGIN IF NEW.Grade < 0 OR NEW.Grade > 100 THEN RAISE EXCEPTION 'grade out of range: %', NEW.Grade; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_sc_grade BEFORE INSERT OR UPDATE ON SC FOR EACH ROW EXECUTE PROCEDURE check_grade();

触发器里的NEW指代即将写入的新行,BEFORE表示在真正落盘前执行,FOR EACH ROW表示逐行触发。这套东西能做的事比CHECK多,代价是它把业务规则藏进了数据库,出问题时从应用代码里翻不到痕迹,排查成本高。我的习惯是:能用CHECK表达的绝不写触发器,只有需要跨表判断或者记录审计日志时才上。

安全性控制这边,核心是用户、角色、授权三件套:

-- 建一个能登录的用户(openGauss 里 CREATE USER 默认带 LOGIN) CREATE USER u_student IDENTIFIED BY 'Str0ng#Pass2024'; -- 建一个只读角色,作为权限的集合 CREATE ROLE r_readonly; GRANT SELECT ON Students, Courses, SC TO r_readonly; -- 把角色授予用户,用户就继承了这批判权限 GRANT r_readonly TO u_student; -- 收回时对准角色,比逐个表收干净 REVOKE SELECT ON Courses FROM r_readonly;

角色这个东西的意义在人多的时候才体现出来。十个学生用户要查同样的三张表,用角色授权只需两条语句;以后要加一张表,改角色一处,十个人同时生效。报告里那段「仓库管理员」的辨析说的就是这个——用户是具体账户,角色是权限模板。另外注意REVOKE的作用对象:从角色上收回,所有继承该角色的用户同步失效;从单个用户上收回,只影响他一个人。

4.3 事务、并发与备份恢复:把「不出事」做成可验证的

实验七练事务,重点不是COMMIT和ROLLBACK这两个词,而是你亲眼看到「没提交的数据别人看不到」这件事。

-- 会话 A BEGIN; UPDATE SC SET Grade = Grade + 5 WHERE Cno = 'C01'; SAVEPOINT sp1; -- 打个存档点 UPDATE SC SET Grade = Grade + 5 WHERE Cno = 'C02'; ROLLBACK TO sp1; -- 只回滚到存档点,C01 的修改还在 -- 此时另开一个 gsql 窗口查 SC,看不到任何变化 COMMIT; -- 提交后另一个窗口才查得到

SAVEPOINT是很多人没用过但很实用的东西,它允许你在一个长事务里做部分回滚,不用整段重来。并发这边,开两个窗口对同一行做UPDATE,第一个不提交,第二个会一直等锁,等到超时或者第一个提交。观察锁状态用SELECT * FROM pg_locks;,看等待关系用pg_stat_activity。真跑一遍这个实验,比背十页锁机制都管用。

实验八的备份恢复,报告里给的是思路,具体命令按常见做法是这样:

# 逻辑备份成纯 SQL 文本,可读性好,适合小库和跨版本迁移 gs_dump -U omm -W -p 26000 -f /home/omm/backup/db_school.sql db_school # 自定义格式备份,压缩率高、支持并行恢复,生产上更常用 gs_dump -U omm -W -p 26000 -F c -f /home/omm/backup/db_school.tar db_school # 恢复到另一个库,先建空库再灌 gs_restore -U omm -W -p 26000 -d db_school_bak /home/omm/backup/db_school.tar

备份这件事最大的坑不是命令写错,是「备了但从没恢复过」。写完报告里的备份步骤,一定顺手恢复到另一个库里,跑一条SELECT COUNT(*)对一下行数,这个动作花不了两分钟,但能在真出事的时候救你一命。

4.4 JDBC:用 Java 把前八次实验串起来

实验九是全篇的收口:从外部程序连回数据库。驱动 jar 常见做法是引opengauss-jdbc,连接串形态和 PostgreSQL 很像,把协议头换成jdbc:opengauss即可。

String url = "jdbc:opengauss://127.0.0.1:26000/db_school"; // 驱动类名按驱动版本可能不同,常见的是 org.opengauss.Driver Class.forName("org.opengauss.Driver"); try (Connection conn = DriverManager.getConnection(url, "u_student", "Str0ng#Pass2024"); PreparedStatement ps = conn.prepareStatement( "SELECT Sno, Sname FROM Students WHERE Dno = ?")) { ps.setString(1, "D01"); // 参数从 1 开始编号,不是 0 try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { System.out.println(rs.getString(1) + "\t" + rs.getString(2)); } } }

PreparedStatement用占位符而不是拼字符串,一是防注入,二是同一条语句可以被数据库缓存执行计划,批量跑的时候快不少。try-with-resources保证Connection、Statement、ResultSet三层都会关,手写finally很容易漏掉最里面那层。这一段代码虽然短,但它验证的是整条链路:网络通、端口对、用户有权限、库里有表、表里有数据,前面八个实验任何一环出错,这里都会以异常的形式暴露出来。

5. 避坑与排查:跑这套实验最容易翻车的五个点

5.1 gsql 敲下去提示找不到命令

现象:在终端里执行gsql -d postgres -p 26000,返回command not found,或者提示无法加载共享库。原因基本只有一个——你不在安装用户的环境里。数据库的可执行文件和库路径是靠安装用户家目录下的环境变量指过去的,用 root 直接敲,PATH 里根本没有这些路径。

解决分两步。先确认切换方式:su - omm中间那个减号不能省,它让 shell 走登录模式去读 profile;写成su omm是不走登录 shell 的,环境变量不会加载。如果切过去还是没有,手动source ~/.bashrc或者source ~/.bash_profile一次,再用echo $GAUSSHOME确认非空。这一步做完还是不行,那就是安装时环境变量压根没写进去,回去补写。

5.2 实例起来了但连不上,卡在连接超时

现象:gs_om -t status --detail显示实例 running,gsql却连不上,或者外部用 JDBC 连的时候超时。原因有三个层次:一是端口不是你以为的那个,gs_om -t status --detail里能看到实际端口,别照抄教程里的 5432;二是服务监听在某个具体网卡上,本机127.0.0.1通、外部 IP 不通;三是pg_hba.conf里没有放行你的来源地址。

排查顺序建议从里往外:先在本机用gsql连,通了再试127.0.0.1,再试真实 IP,最后才试外部机器。外部连不上时,重点看pg_hba.conf里有没有对应的host all all <网段> sha256条目,改完要gs_om -t reload生效,重启实例也行但没必要。这个文件改错一行,认证会直接拒绝,而报错信息只写「no pg_hba.conf entry」,不给提示是哪个来源,只能自己对。

5.3 建外键时报「没有唯一约束匹配」

现象:建Students表时给Dno加外键,引用Departments(Dno),报错说找不到与被引用列匹配的唯一约束。原因通常是两种:被引用的列确实没建主键或唯一约束;或者两边的列类型不完全一致,比如一边是CHAR(20)、另一边是VARCHAR(20),长度也不同。

解决办法是先回去查被引用表的结构,用\d+ Departments看清楚Dno到底是什么类型、有没有主键。类型不一致的,把两边改齐,或者用ALTER TABLE ... ALTER COLUMN ... TYPE统一。另一种常见情况是循环引用导致建表顺序无解,这时不要硬凑顺序,直接先建表、后ALTER TABLE ADD CONSTRAINT补外键,两步走。

5.4 索引建了,执行计划还是全表扫描

现象:CREATE INDEX执行成功,\di也能看到,但EXPLAIN出来的还是Seq Scan。原因有四种:表里数据太少,优化器算完成本认为全表扫更划算;查询条件里的列类型和索引列不一致,发生了隐式转换;WHERE里对索引列套了函数,比如WHERE UPPER(Sno) = ...;统计信息太旧,优化器对数据分布判断错误。

对应处理:灌进去几百行真实感的数据;把查询条件里的字面量和列类型对齐;把函数从列上挪到值上,改写成WHERE Sno = UPPER('...');手动执行一次ANALYZE Students;刷新统计。做完这些还不行,就EXPLAIN看完整的成本估算,别只看第一行。

5.5 用户建好了,JDBC 还是连不上

现象:实验六里已经把用户建好、权限也授了,gsql 用这个用户在本机连得通,Java 程序一跑就报认证失败或无入口。原因是 gsql 本机连接走的可能是本地 socket,而 JDBC 走的是 TCP,两条路径的认证规则分别写在pg_hba.conf的不同条目里。本地条目放行了,不等于 TCP 条目也放行了。

解决就是去pg_hba.conf里补host开头的条目,指定库、用户、来源网段和加密方式,然后 reload。另外顺手确认一下这个用户的密码有没有过期策略——有些环境会强制首次登录改密,程序里拿旧密码连,自然连不上。这条坑我在做期末演示前踩过一次,现场很尴尬,从那以后每次演示前都先用最简的 Java 程序连一遍。

6. 二次开发:把九个实验收成一个可复跑的 SQL 脚本仓库

这份报告最可惜的地方,是它的成果全躺在 Word 文档和截图里。真正能复用的是那些 SQL 语句,而它们散落在九章的正文中间,想重跑一遍得靠人工翻页。我一般会做一次结构改造:把每个实验的语句按执行顺序抽成脚本文件,用目录编号把依赖关系写死。

sql-lab/ ├── 00_env.sql # 建表空间、建库、建用户 ├── 01_schema.sql # 建模式 ├── 02_tables.sql # 建表、补外键约束 ├── 03_index.sql # 建索引 ├── 04_data.sql # 灌测试数据 ├── 05_view.sql # 视图 ├── 06_integrity.sql # 触发器与约束补充 ├── 07_security.sql # 角色与授权 ├── 08_transaction.sql # 事务与并发样例 ├── 99_teardown.sql # 清库,方便反复重跑 └── run_all.sh # 顺序执行入口

对应的执行入口很短,但把「一键重建」这件事解决了:

#!/usr/bin/env bash # 按编号顺序执行,任一步失败就整体中止,避免脏状态继续往下跑 set -e for f in $(ls [0-9]*.sql | sort); do echo ">>> running $f" gsql -d db_school -p 26000 -f "$f" done

set -e是这个脚本里最重要的一个字,没有它,第三步建表失败了脚本照样往下跑,后面报一堆「表不存在」,把真正的错误淹没掉。ls [0-9]*.sql | sort靠文件名编号保证顺序,比在脚本里手写一串文件名好维护得多——加一个实验就加一个文件,不用改脚本主体。

改造完的验证方法只有一个:在干净环境里从99_teardown.sql开始,把整套脚本跑一遍,跑到最后实验九的 Java 程序能查出数据为止。这一步能暴露的问题远比想象中多,比如某个约束依赖了别处建的对象、某张表忘了指定模式、某条INSERT的顺序和外键方向反了。我第一次做这种拆分时,就因为SC表的测试数据插在Courses之前,一路跑到第四步才炸。从那以后我每次整理这类脚本都强制走一遍「清库—重建—验证查询」的闭环,确认干净环境下能一次跑通,才敢把脚本交给别人。这套习惯用在课程实验报告上是整理,用在真实项目上就是能救命的东西。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询