☰
openGauss数据库实验主线:建库建表、SQL查询、事务与JDBC接入
2026/11/4 16:54:56 网站建设 项目流程

简介:《数据库系统原理实验》报告书面向计算机科学与技术、软件工程专业学生及数据库初学者,配套课程实验与openGauss操作练习,帮助把概念模型、关系模型和SQL命令落到实际DBMS环境中。内容按九个实验展开,从认识DBMS、创建数据库表与索引、交互式SQL、视图、完整性与安全性控制,到事务并发、备份恢复及JDBC连接数据库,均记录实验目的、内容、类型、学时、仪器材料与结果分析,并穿插gsql命令、系统表查询和常见概念辨析。压缩包仅含1个docx文件,约3.23MB,目录清晰,便于按实验章节查阅和打印。已有1527人学习,适合需要完成课程报告、复盘实验步骤或查漏补缺的读者参考。

1. 数据库实验的主干:九个实验其实挂在四条线上

翻开这份《数据库系统原理实验》报告书的目录,九个实验排得整整齐齐,第一眼看上去像九个小练习拼在一起。真按顺序做一遍会发现,它其实只有一条主干:一台数据库实例从装起来到被程序连上去。挂在这条主干上有四组内容——环境与对象(实验一、二)、查询与视图(实验三、四)、控制与恢复(实验五到八)、程序接入(实验九)。前三组决定数据库“长什么样、谁能动、坏了怎么办”,最后一组决定它能不能被应用层用起来。适合正在上数据库原理实验课、准备期末上机或者复试机试的人:先把这张图记住,再往下抠细节,比按实验号死记顺序省力得多。

2. 环境与对象:openGauss 从装库到建表建索引的顺序

2.1 三种安装路径与 gsql 的连接入口

实验一真正要考的不是“装成功了没有”,而是你知不知道 DBMS 处在哪一层、客户端是怎么连上来的。常见的三种装法:云上托管安装、虚拟机一键安装、先装好 Linux 再手工装数据库。选哪种取决于报告要写什么——想省事选一键,想把安装用户、数据目录、端口这些来龙去脉写清楚,选第三种,因为每一步都是你自己敲的,出问题也知道去哪查。

连接链是三段:先切到数据库安装用户,再确认实例起来了,最后用客户端连上去。切用户不能用 root 直接跑服务,这是安装时就已经定好的规矩;服务没起来的时候,gsql 报的是连接失败,而不是语法错误,很多人第一反应去改 SQL,方向就错了。

# 切到数据库安装用户环境,- 的作用是把该用户的环境变量一起带过来 su - omm # 查看实例状态并启动;-t 表示对实例做操作,后面跟具体动作 gs_om -t start # 连接默认库 postgres,主节点端口 26000 gsql -d postgres -p 26000 -r

-d指定要连的库,安装完默认会生成一个 postgres 库,初始只能先连它;-p是主节点端口,26000 是规划时定下的值,端口记错是最常见的连不上原因;-r影响交互时的回显行为,具体表现以gsql --help的输出为准,别照抄别人的参数。连上以后第一件事不是建表,是用元命令把环境摸一遍,这些反斜杠命令在写报告截图时特别有用。

元命令作用典型使用场景
\?元命令帮助忘了某个反斜杠命令怎么拼
\hSQL 语法帮助记不清 CREATE VIEW 的完整选项
\l列出所有数据库确认建库那一步到底成没成
\c dbname切换数据库建完库立刻切过去验证
\dn列出模式确认 schema 的属主是谁
\dt列出当前库的表六张表建完的一次总检查
\d+ tablename表结构加存储信息验证约束有没有真的加上
\di列出索引区分主码自动索引和自建索引
\db列出表空间确认表空间落在哪个目录
\q退出收尾

这里有个概念要在实验一就分清:用户是具体的登录账户,角色是一组权限的集合。给某个人开账户是建用户,给“一类人”划权限是建角色,把角色授予用户,用户就一次性拿到了这批权限。新人入职换岗,改的是角色授予关系,不用一条条改权限。这个区分在后面实验六会反复用到,实验一没弄明白,到权限那节会卡住。

2.2 表空间、数据库、模式、表:四层容器怎么套

教材里那个“表空间像文件夹”的类比是对的,但要接着往下说:表空间是物理存储的划分,数据库是建在某个表空间上的一块逻辑空间,模式是库里的一组命名对象,表是模式里的具体对象。一个数据文件只能属于一个表空间,一个表空间可以有多个数据文件;一个库可以有很多模式,一个模式可以有很多表。

-- 表空间要指向一个已经存在的空目录,RELATIVE LOCATION 是相对数据目录的路径 CREATE TABLESPACE tpcds_local RELATIVE LOCATION 'tablespace/tablespace_1'; -- 建库时把默认表空间指定过去,不指定就用默认表空间 CREATE DATABASE db_sc WITH TABLESPACE = tpcds_local; -- 库改名,名字改了但库里的对象不受影响 ALTER DATABASE db_sc RENAME TO db_school;

RELATIVE LOCATION后面必须是空目录,目录里已经有文件时会直接报错;WITH TABLESPACE决定这个库的对象默认往哪儿放。改名之后记得\c db_school切过去,不然接下来建的表全落在 postgres 库里,报告里一对截图就露馅。

模式的两种含义是这一节最容易混的地方。概念上的模式指全体数据的逻辑结构和特征描述,是所有用户的公共数据视图,一个数据库只有一个;物理上的模式指库里一个名字空间,装着一组表、视图、存储过程。实验里建的是后者。

CREATE SCHEMA book_eg; -- 建一个名字空间 CREATE TABLE book_eg.Teachers (Tno CHAR(20)); -- 对象挂在这个空间下,用 模式名.表名 访问 DROP SCHEMA book_eg CASCADE; -- 级联删除:模式连同里面所有对象一起没了

最后那句CASCADE是把双刃剑:模式里还有表的时候,不加CASCADE会拒绝删除,加了就一路清空。做实验时它很省事,真在有人用的库上敲,就是一次事故。稳妥做法是先\dt book_eg.*看一眼里面有什么,确认是空的再删。

2.3 建表与约束:列级和表级的区别先分清

建表前必须先把列级约束和表级约束分清楚,这是后面所有约束实验的地基。

对比项列级约束表级约束
书写位置紧跟在列定义后面所有列定义写完之后,独立成句
是否指明列名不需要,默认就是当前列必须写明作用在哪几列上
能否命名一般不能可以用 CONSTRAINT 起名
适用场景单列的非空、默认、检查、主码复合主码、多列检查、外键

教材里有一句结论值得记住:涉及多个属性列的约束必须写成表级,只涉及一列的写在列级或表级都行,只是语句形式不同。实验里六张表——教师、院系、学生、课程、选课、授课——正好把这几种情况都覆盖了一遍。

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里同时写完——先建的那张表引用不到还不存在的表。标准解法是先建表、后补约束:

-- 两张表都建好之后,再补上 Teachers 指向 Departments 的外码 ALTER TABLE Teachers ADD CONSTRAINT fk_teachers_dept FOREIGN KEY (Dno) REFERENCES Departments(Dno);

ADD CONSTRAINT后面跟的是约束名,起名字的好处是将来报错信息里能直接看到是哪条约束拦下来的,DROP CONSTRAINT时也不用去猜系统自动生成的名字。

索引这部分,主码在建表时就已经自动建了唯一索引,不需要再手工建一遍。手工建索引只针对高频查询列:

-- 学生表按院系号查得很频繁,在 Dno 上建普通索引 CREATE INDEX Student_Dept ON Students(Dno);

建完用\di查看,能看到主码索引和这个索引并存。索引能加快检索,代价是每次插入、删除、更新都要维护它;写多读少的表上堆一堆索引,反而拖慢整体。另外字段类型也别乱选:无符号 SMALLINT 的范围是 0 到 65535,INT 是 4 字节,在够用的前提下优先小类型,这条在课程表这种字段少的表上体现不出来,数据量一大就有区别。

3. 查询与视图:交互式 SQL 的骨架和虚表的边界

3.1 交互式 SQL:先背分类,再背执行顺序

实验三看着是“多敲几条查询”,实际考的是你能不能把 SQL 按用途分类,并且在写复杂查询时知道每一步在干什么。

类别作用代表语句
DDL 数据定义定义和修改对象CREATE / ALTER / DROP
DML 数据操纵增删改数据INSERT / UPDATE / DELETE
DQL 数据查询检索数据SELECT
DCL 数据控制授权与回收GRANT / REVOKE
TCL 事务控制事务边界BEGIN / COMMIT / ROLLBACK / SAVEPOINT

比分类更容易被忽略的是 SELECT 的逻辑执行顺序:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。这个顺序解释了两个老问题:为什么 WHERE 里不能用 SELECT 中起的列别名(那时候别名还没生成),为什么聚合条件必须写 HAVING 而不能写 WHERE(WHERE 在分组之前执行,那时还没有聚合结果)。

SELECT c.Cno, c.Cname, COUNT(sc.Sno) AS stu_cnt, AVG(sc.Grade) AS avg_grade FROM Courses c JOIN SC sc ON c.Cno = sc.Cno -- 连接条件写在 ON 上,和过滤条件分开 WHERE sc.Grade IS NOT NULL -- 行级过滤:先排掉空成绩 GROUP BY c.Cno, c.Cname -- 分组列必须覆盖 SELECT 里所有非聚合列 HAVING COUNT(sc.Sno) >= 2 -- 组级过滤:选课人数不足两人的课不要 ORDER BY avg_grade DESC; -- 排序可以用别名,因为它在 SELECT 之后

分组列必须覆盖所有非聚合列,这条规则违反了会直接报错,不报错的数据库是在纵容你写出结果不确定的 SQL。COUNT(sc.Sno)和COUNT(*)的区别在于前者不数 NULL,统计“实际有成绩的人数”时只能用前者。

NULL 的处理是这一节的血泪经验集中区。NULL 不是 0 也不是空串,它表示未知,参与任何比较的结果都是未知:Grade = NULL永远不成立,必须写Grade IS NULL;NOT IN的子查询里只要出现一个 NULL,整个条件就可能一条都查不出来。写查询时把IS NULL、COALESCE、COUNT(列)这三个工具备着,能省掉大量莫名其妙的空结果。

3.2 视图:虚表的价值和不能动的地方

视图不存数据,只把一条查询的定义存进数据字典,每次查询视图时再展开执行。它解决三件事:把多表连接藏起来让上层写起来简单、只暴露部分行和列从而控制可见范围、表结构变了可以用视图在外层兜住。

-- 行列子集视图:只取部分列,做连接后给上层一个扁平结构 CREATE VIEW v_student_dept (Sno, Sname, Dname) AS SELECT s.Sno, s.Sname, d.Dname FROM Students s JOIN Departments d ON s.Dno = d.Dno; -- 带检查选项的视图:通过视图写入的数据必须满足 Grade >= 60 CREATE VIEW v_sc_pass AS SELECT * FROM SC WHERE Grade >= 60 WITH CHECK OPTION;

列名列表写在视图名后面,可以给列改名,不写就沿用查询结果的列名。WITH CHECK OPTION是很多人漏掉的一句:不加它,通过视图插入一条 Grade 为 50 的记录也能成功,插完再查视图却看不见这条数据——数据在表里,但不在视图的可见范围里,这种“插了等于没插”的现象最难排查。

对比项表视图
是否存储数据存不存,只存定义
能否建索引能一般不能
能否更新能取决于定义,行列子集视图通常可以
主要价值持久化数据简化查询、控制可见范围、屏蔽结构变化

可更新视图的判断标准:定义里含聚合函数、GROUP BY、DISTINCT、集合运算的,基本都不可更新,因为一行视图数据对应不到唯一一行基表数据。实验里如果要求通过视图改数据,先确认定义干不干净,改不动不是权限问题,是视图本身不支持。

4. 控制与连接:完整性、安全性、事务恢复和 JDBC 的四道关

4.1 完整性控制:三类约束和违约处理

完整性约束分三类:实体完整性管主码非空唯一,参照完整性管外码必须引用存在的主码,用户定义完整性管业务规则(性别只能是男女、成绩在 0 到 100 之间)。前两类是数据库自动维护的,第三类要靠 CHECK、NOT NULL、DEFAULT、UNIQUE 这些写出来。

外码的关键在违约处理,也就是主表数据被删被改时,从表怎么办:

违约处理行为适用场景
RESTRICT / NO ACTION有从表记录引用就拒绝删除默认、最安全
CASCADE主表删,从表跟着删强从属关系,如选课记录随学生删
SET NULL从表外码置空关系可以断,但记录要留
-- 学生删除时,他的选课记录跟着删;学生学号改了,选课表里的学号同步更新 ALTER TABLE SC ADD CONSTRAINT fk_sc_student FOREIGN KEY (Sno) REFERENCES Students(Sno) ON DELETE CASCADE ON UPDATE CASCADE;

级联删除做实验很爽,一条 DELETE 清掉一片,但它不会问你确认。选 CASCADE 之前先想清楚这张从表的数据是不是可以随手丢。SET NULL 的前提是从表那一列允许为空,列上有 NOT NULL 就会直接报错。约束表达不了的复杂规则才考虑写触发器,能用约束解决的不要上触发器——触发器是隐式执行的,出问题时最难定位。

4.2 安全性控制:用户、角色、权限

安全性控制和完整性控制在报告里经常被写成一段,其实是两个方向:完整性防的是数据不合规,安全性防的是人越权。手段就三样:建用户、建角色、授权回收。

CREATE USER u_reader IDENTIFIED BY '按密码策略设置'; -- 建登录账户 CREATE ROLE r_query; -- 建权限集合 GRANT SELECT ON Students, Courses TO r_query; -- 先把权限给角色 GRANT r_query TO u_reader; -- 再把角色授予用户 REVOKE SELECT ON Courses FROM r_query; -- 收回其中一张表的查询权

先给角色再给用户,好处是权限模型跟着岗位走而不是跟着人走。WITH GRANT OPTION让被授权者可以继续往下授权,实验里用来演示权限传递,真实环境里要慎用,不然权限收不回来。查当前有哪些用户和角色,直接查系统表pg_user、pg_roles比记元命令稳。

权限粒度不止表一级:库、模式、表、列都能单独授权。漏给模式的使用权限,用户连表名都解析不到,报的错却像表不存在,这是排查权限问题时最常见的误导。

4.3 事务与并发:ACID 落到实验里的样子

事务是一组要么全做要么全不做的操作,ACID 四个特性在实验里对应四个可观察的现象:原子性看回滚后数据有没有复原,一致性看约束有没有被破坏,隔离性看并发事务之间看不看得见彼此的中间状态,持久性看提交后断电数据还在不在。

BEGIN; -- 开启事务 SAVEPOINT sp1; -- 设一个回滚点 UPDATE SC SET Grade = Grade + 5 WHERE Cno = 'C01'; -- 第一步修改 ROLLBACK TO sp1; -- 只撤销到回滚点,事务还没结束 COMMIT; -- 提交

SAVEPOINT和ROLLBACK TO是部分回滚,适合一个事务里有多段独立操作的场景;ROLLBACK不带 TO 就是整个事务作废。这两者混用是实验里常见的扣分点:以为回滚到保存点就等于结束了事务,后面又 COMMIT 一次,结果把本该丢弃的修改提交上去了。

并发异常有四类,隔离级别就是用来说清楚“能容忍哪几种”的:

异常现象最低能挡住它的隔离级别
脏读读到别的事务未提交的数据READ COMMITTED
不可重复读同一事务内两次读同一行结果不同REPEATABLE READ
幻读同一事务内两次读同一范围行数变了SERIALIZABLE
丢失更新两个事务覆盖写,后写的盖掉先写的靠锁或乐观版本号

用SHOW transaction_isolation;查当前隔离级别,改级别再跑一遍两个会话的交叉操作,异常现象就能亲眼看到。死锁的成因几乎都是两个事务加锁顺序相反,处理办法不是调参,是统一访问顺序、把事务写短。

4.4 备份恢复与 JDBC 接入

备份分逻辑备份和物理备份。实验八一般做逻辑备份:导出整个库的 SQL 或自定义格式文件,删掉几张表模拟故障,再恢复回来比对数据。

# 逻辑备份:-F c 表示自定义格式(压缩且可选择性恢复),-f 指定输出文件 gs_dump -d db_school -p 26000 -F c -f /home/omm/db_school.dmp # 恢复到一个新建的空库,-d 指定目标库 gs_restore -d db_school_bak -p 26000 /home/omm/db_school.dmp

-F c的自定义格式比纯 SQL 文本好在可以只恢复某几张表;-f是输出路径,注意执行用户对该目录得有写权限。恢复到非空库经常报对象已存在,规范做法是先建一个空库再往里恢复,恢复完用行数比对验证,别只看命令返回成功。物理备份和 WAL 归档支撑的是时间点恢复,实验里了解思路即可。

实验九用 JDBC 把前面所有东西串起来:程序连库、执行 SQL、控制事务。

// 驱动类名随版本不同,以实际 jar 包里的类为准 Class.forName("org.opengauss.Driver"); String url = "jdbc:opengauss://127.0.0.1:26000/db_school"; try (Connection conn = DriverManager.getConnection(url, user, pwd)) { conn.setAutoCommit(false); // 关掉自动提交,事务边界自己控 try (PreparedStatement ps = conn.prepareStatement( "INSERT INTO SC(Sno, Cno, Grade) VALUES (?, ?, ?)")) { ps.setString(1, "2021001"); // 参数下标从 1 开始,不是 0 ps.setString(2, "C01"); ps.setInt(3, 88); ps.executeUpdate(); conn.commit(); // 全部成功才提交 } catch (SQLException e) { conn.rollback(); // 任何一步失败整体回滚 } }

用PreparedStatement而不是拼字符串,一是防注入,二是同一条语句反复执行时能复用执行计划。连接串里的端口必须和实例端口一致,库名必须已经存在;setAutoCommit(false)之后忘了commit(),程序跑完看着没报错,数据一条没进去,这是 JDBC 实验最常见的空欢喜。

5. 易错点复盘:建表、权限、事务里最容易翻车的地方

5.1 对象与约束环节

易漏点一:互相引用的外键直接写在 CREATE TABLE 里。为什么漏——教材上的例子基本都是单向引用,照着抄不会出问题,一旦两张表互相引用,先建的那张必然找不到对方。怎么补——凡是有循环引用,先建两张不带外码的表,再用ALTER TABLE ... ADD CONSTRAINT补上,约束起个名字,报错时一眼看出是哪条。

易漏点二:DROP SCHEMA ... CASCADE顺手就敲。为什么漏——模式里没东西时加不加 CASCADE 结果一样,养成习惯以后,模式里有表也照敲不误。怎么补——删之前先\dt 模式名.*看一眼,默认不加 CASCADE,让数据库替你拦一道。

易漏点三:多列约束写成列级。为什么漏——单列约束写习惯了,复合主码或者跨列的 CHECK 也往列后面一贴,语法直接不过。怎么补——记住一条判定线:约束涉及两列及以上,一律写表级,位置在所有列定义之后。

易漏点四:索引当万金油,见列就建。为什么漏——建索引后查询确实快了,看不到的是写入变慢、占用变大。怎么补——只在 WHERE、JOIN、ORDER BY 里高频出现的列上建,主码自带索引不要重复建,建完用执行计划确认它真的被用上了。

5.2 权限、事务与连接环节

易漏点五:权限只授到表,忘了模式和序列。为什么漏——GRANT 语句写对了,用户还是报“表不存在”,容易往表名拼写上去找原因。怎么补——授权时按“库 → 模式 → 表”逐层确认,模式要有 USAGE,用到自增序列的还要给序列权限;报错信息里出现 permission denied 就是权限问题,别去改 SQL。

易漏点六:事务开了不关,或者回滚点用错。为什么漏——交互式客户端里不提交也能看到自己改的数据,误以为已经生效;JDBC 里关掉自动提交后忘了 commit,程序退出数据全丢。怎么补——成对写:BEGIN 配 COMMIT 或 ROLLBACK,setAutoCommit(false)后面必须跟 try/catch 里的 commit 和 rollback,写的时候就把骨架摆好,别事后补。

易漏点七:把 NULL 当 0 或者空串用。为什么漏——插入时没赋值,查出来是空白,直觉上就当它是空值参与运算了。怎么补——凡是可能为空的列,比较一律用IS NULL,统计用COUNT(列)明确要不要数空值,参与计算的用COALESCE兜底。

6. 复习清单:按九个实验逐条自检

复习这份报告书,我建议不要按页翻,按“能不能独立复现”来自检。下面这张表每行对应一个实验,右列是自检问题,能答上来再往下走。

实验一句话考点自检问题
一 认识 DBMS客户端如何连上实例端口记错报什么错?切换用户的-有什么用?
二 库表索引四层容器的嵌套关系循环外键为什么必须后加?表空间目录非空会怎样?
三 交互式 SQL执行顺序与 NULL 语义为什么 WHERE 里不能用列别名?NOT IN遇 NULL 会怎样?
四 视图虚表的可更新边界哪些定义会让视图不可更新?WITH CHECK OPTION挡的是什么?
五 完整性三类约束与违约处理CASCADE 和 SET NULL 各自的前提是什么?
六 安全性用户、角色、权限粒度为什么不直接给用户授权而要先建角色?
七 事务并发ACID 与隔离级别回滚到保存点算不算事务结束?脏读和幻读差在哪?
八 备份恢复逻辑备份与恢复验证恢复到非空库为什么会失败?怎么验证恢复成功?
九 JDBC连接、预编译、事务参数下标从几开始?忘了 commit 会有什么现象?

查漏补缺的具体做法:挑三个最容易混的点做对照实验,一次只改一个变量。比如把隔离级别从默认调到可重复读,用两个会话同时读写同一行,观察第二次读的结果变没变;比如把外键的 CASCADE 改成 RESTRICT,再删一次主表数据,看报错信息长什么样。做过对照,记忆才不是靠背。

代码题的自检标准更简单:合上报告,从空库开始,把六张表、约束、索引、两个视图、一次授权、一次事务回滚、一次备份恢复、一个 JDBC 插入,全部默写一遍。卡在哪一步,那一节就是漏洞。

我自己的教训是:第一次做实验七时,回滚到保存点之后又 COMMIT 了一次,把本该丢弃的修改提交上去了,对着结果找了半小时才反应过来。从那以后我每次开事务都先把 BEGIN、SAVEPOINT、ROLLBACK、COMMIT 四行的骨架敲完再往里填内容,写完立刻在两个会话里验一遍隔离级别的表现。这套流程后来在真实项目里救过我好几次,希望帮到你。

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

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

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

立即咨询