Oracle SQL 基础复习:表管理、数据操作与多表查询
SQL 可以分成几类常见操作。学习数据库时,先把这些操作分清楚,会比直接背语句更容易建立整体结构。
| 类型 | 作用 | 常见语句 |
|---|---|---|
| DDL | 定义或修改数据库对象 | CREATE TABLE、ALTER TABLE、DROP TABLE |
| DML | 操作表中的数据 | INSERT、UPDATE、DELETE |
| DQL | 查询数据 | SELECT |
| TCL | 控制事务 | COMMIT、ROLLBACK |
本笔记使用 Oracle SQL 语法,和 MySQL 有一些差异。例如 Oracle 常用 NUMBER、VARCHAR2、TO_DATE,查看表结构时也会用到 USER_TAB_COLUMNS。
查看当前用户下的表
Oracle 中可以用:
SELECT *FROM tab;这条语句用于查看当前用户 schema 下已有的表、视图等对象。
在 DBeaver 中,DESC table_name 可能不可用。可以改用数据字典表查询字段信息:
SELECT column_name, data_typeFROM user_tab_columnsWHERE table_name = 'STUDENT';注意:Oracle 默认会把未加双引号的表名、字段名转成大写,因此这里写 'STUDENT'。
创建表:CREATE TABLE
创建表时,需要定义字段名、数据类型、是否允许为空,以及主键等约束。
示例:
CREATE TABLE lecturer ( staffno NUMBER(6) NOT NULL, title VARCHAR2(3), fname VARCHAR2(30), lname VARCHAR2(30), streetaddress VARCHAR2(70), suburb VARCHAR2(40), city VARCHAR2(40), postcode VARCHAR2(4), country VARCHAR2(30), lecturerlevel CHAR(2), bankno CHAR(20), bankname VARCHAR2(40), salary NUMBER(8,2), workload NUMBER(2,1) NOT NULL, researcharea VARCHAR2(40), PRIMARY KEY (staffno));几个常见数据类型:
| 类型 | 含义 |
|---|---|
NUMBER(6) | 最多 6 位数字 |
NUMBER(8,2) | 总共 8 位数字,其中 2 位小数 |
VARCHAR2(30) | 可变长度字符串,最多 30 个字符 |
CHAR(2) | 固定长度字符串 |
DATE | 日期时间类型 |
PRIMARY KEY 表示主键。主键字段不能重复,也不能为空,通常用于唯一标识一行记录。
插入数据:INSERT INTO
插入数据有两种常见写法。
第一种:指定字段名。
INSERT INTO lecturer ( staffno, title, fname, lname, streetaddress, suburb, city, postcode, country, lecturerlevel, bankno, bankname, salary, workload, researcharea) VALUES ( 1000, 'Dr', 'David', 'Taniar', '3 Robinson Av', 'Kew', 'Melbourne', '3080', 'Australia', '5', '1000567237', 'CommBank', 89000.00, 2.0, 'O-R DB');第二种:不指定字段名,直接按表结构顺序插入。
INSERT INTO lecturerVALUES ( 3000, 'Mr', 'Daniel', 'Wright', '22 Crystal Cres', 'Alphington', 'Melbourne', '3790', 'Australia', '5', '1000654321', 'CommBank', 89000.00, 2.0, 'DB');第二种写法更短,但依赖字段顺序。一旦表结构变化,语句更容易出错。因此在正式脚本中,通常更推荐显式写出字段名。
主键重复错误
如果主键已经存在,再插入相同主键,会报错。
例如已经存在 staffno = 1000,再次插入同样的 staffno:
INSERT INTO lecturer (...)VALUES (1000, ...);会违反主键唯一性约束。
处理方法不是绕过主键,而是确认数据是否应该是新记录。如果是新讲师,应使用新的 staffno,例如:
VALUES (2000, ...);主键冲突是数据库中很常见的问题。排查时先看:
插入的主键值是否已经存在业务上是否应该更新旧记录是否误把新增操作写成重复插入部分字段插入
如果只插入部分字段,必须写出字段名,并且所有 NOT NULL 字段都必须提供值。
INSERT INTO lecturer ( staffno, title, fname, lname, streetaddress, suburb, postcode, country, researcharea, workload) VALUES ( 4000, 'Mr', 'RaiHong', 'Lam', '12 Oracle Dr', 'Fitzroy', '3424', 'Australia', 'Data Mining', 1);没有提供的字段会被设置为 NULL,前提是这些字段允许为空。
日期处理:TO_DATE 与 TO_CHAR
Oracle 中插入日期时,常使用 TO_DATE 把字符串转换成日期。
INSERT INTO studentVALUES ( 30001, TO_DATE('12-FEB-2002', 'DD-MON-YYYY'), 'Alice', 'Brown', 'Melbourne', '3000', 'Australia', 5000.00, TO_DATE('15-JUL-2026', 'DD-MON-YYYY'));如果包含时间,可以写成:
TO_DATE('12-MAR-2001 16:15', 'DD-MON-YYYY HH24:MI')查询时如果要控制日期显示格式,可以用 TO_CHAR:
SELECT TO_CHAR(SYSDATE, 'DD-MON-YYYY HH24:MI')FROM dual;DUAL 是 Oracle 中常用的虚拟表,适合执行不依赖真实业务表的表达式查询。
修改表结构:ALTER TABLE
添加字段:
ALTER TABLE student ADD ( streetaddress VARCHAR2(70), suburb VARCHAR2(40));删除字段:
ALTER TABLE student DROP (citty);添加单个字段:
ALTER TABLE student ADD email VARCHAR2(50);修改字段类型:
ALTER TABLE student MODIFY ( city VARCHAR2(40));在 Oracle 中,ADD 和 DROP 是不同的 ALTER TABLE 操作,通常需要拆成不同语句执行。
ALTER TABLE student ADD email VARCHAR2(50);ALTER TABLE student DROP COLUMN postcode;CHAR 与 VARCHAR2 的区别
CHAR 是固定长度,VARCHAR2 是可变长度。
| 类型 | 特点 | 适合场景 |
|---|---|---|
CHAR(40) | 固定占用 40 个字符长度 | 长度固定的编码、状态位 |
VARCHAR2(40) | 按实际内容长度存储,最多 40 个字符 | 姓名、城市、地址等长度不固定文本 |
例如 city CHAR(40) 即使只存 'Melbourne',也会按固定长度处理。对于城市名这种长度不固定的数据,VARCHAR2(40) 更合适。
更新数据:UPDATE
更新学生地址:
UPDATE studentSET streetaddress = '12 New St'WHERE studentno = 30001;WHERE 非常重要。如果省略 WHERE,会更新整张表:
UPDATE studentSET streetaddress = '12 New St';这类语句在实际环境中风险很高。更新或删除前应先用 SELECT 确认影响范围:
SELECT *FROM studentWHERE studentno = 30001;提交事务:COMMIT
Oracle 中执行插入、更新、删除后,通常需要提交事务:
COMMIT;COMMIT 表示永久保存当前事务中的修改。提交后,不能再通过 ROLLBACK 撤销这些已提交的变化。
常见理解:
INSERT / UPDATE / DELETE 只是产生修改COMMIT 才是确认保存ROLLBACK 用于撤销未提交修改复制已有表:CREATE TABLE AS SELECT
Lab 中使用了 CREATE TABLE AS SELECT,简称 CTAS,用于从已有表复制数据到新表。
CREATE TABLE subject ASSELECT *FROM dtaniar.subject;这条语句会根据查询结果创建一张新表,并把数据复制过来。
如果要复制多个基础表,可以依次执行:
CREATE TABLE student AS SELECT * FROM dtaniar.student;CREATE TABLE lecturer AS SELECT * FROM dtaniar.lecturer;CREATE TABLE lecture AS SELECT * FROM dtaniar.lecture;CREATE TABLE tutor AS SELECT * FROM dtaniar.tutor;CREATE TABLE lab AS SELECT * FROM dtaniar.lab;CREATE TABLE student_enrolment AS SELECT * FROM dtaniar.student_enrolment;CREATE TABLE lab_signup AS SELECT * FROM dtaniar.lab_signup;复制完成后可以统计每张表的数据量:
SELECT COUNT(*)FROM student;基础查询:WHERE 与 IN
查询第一学期开设的课程:
SELECT subjectcode, nameFROM subjectWHERE semester = 1;查询出生日期在某个范围内的学生:
SELECT fname, lname, dob, feepaidFROM studentWHERE dob > TO_DATE('31-DEC-1990', 'DD-MON-YYYY') AND dob < TO_DATE('01-JAN-1995', 'DD-MON-YYYY');查询多个课程代码可以用 IN:
SELECT s.studentno, s.fname, s.lname, se.subjectcodeFROM student sJOIN student_enrolment se ON s.studentno = se.studentnoWHERE se.subjectcode IN ('CSE21DB', 'CSE31DB', 'CSE41FDB')ORDER BY se.subjectcode, s.studentno;IN 适合表达“字段值属于某个集合”。
多表查询:JOIN
列出讲师及其授课安排:
SELECT l.staffno, l.title, l.fname, l.lname, le.subjectcode, le.lectday, le.lecttime, le.venueFROM lecturer lLEFT JOIN lecture le ON l.staffno = le.staffnoORDER BY l.staffno, le.lectday, le.lecttime;这里使用 LEFT JOIN,表示即使某个 lecturer 没有对应 lecture,也仍然保留 lecturer 记录。
常见连接方式:
| JOIN 类型 | 含义 |
|---|---|
JOIN / INNER JOIN | 只返回两边都匹配的数据 |
LEFT JOIN | 保留左表全部数据,右表没有匹配时显示 NULL |
RIGHT JOIN | 保留右表全部数据 |
NOT EXISTS:查找没有关联记录的数据
查询没有授课安排的讲师:
SELECT l.staffno, l.title, l.fname, l.lnameFROM lecturer lWHERE NOT EXISTS ( SELECT * FROM lecture le WHERE le.staffno = l.staffno);NOT EXISTS 常用于找“主表中存在,但关联表中不存在”的记录。
可以理解为:
对每一个 lecturer检查 lecture 表里是否存在对应 staffno如果不存在,就返回这个 lecturer聚合函数:AVG、MIN、MAX、COUNT、SUM
计算讲师平均工资:
SELECT AVG(salary) AS averagesalaryFROM lecturer;计算最低和最高工资:
SELECT MIN(salary) AS minimumsalary, MAX(salary) AS maximumsalaryFROM lecturer;统计数据库实验课每周成本:
SELECT SUM(l.duration * t.salaryperhour) AS totalweeklycostFROM lab lJOIN tutor t ON l.tutorno = t.tutornoWHERE l.subjectcode IN ('CSE21DB', 'CSE31DB', 'CSE41FDB');常见聚合函数:
| 函数 | 作用 |
|---|---|
COUNT(*) | 统计行数 |
AVG(column) | 平均值 |
MIN(column) | 最小值 |
MAX(column) | 最大值 |
SUM(column) | 求和 |
GROUP BY:分组统计
统计每门课、每个学期的 tutor 数量:
SELECT s.subjectcode, s.name, s.semester, COUNT(DISTINCT l.tutorno) AS numberoftutorsFROM subject sJOIN lab l ON s.subjectcode = l.subjectcodeGROUP BY s.subjectcode, s.name, s.semesterORDER BY s.subjectcode, s.semester;使用 GROUP BY 时,SELECT 中非聚合字段通常都要出现在 GROUP BY 中。
例如下面这些字段都不是聚合结果:
s.subjectcodes.names.semester所以它们需要一起写进 GROUP BY。
多表连接与分组统计
统计每个 lab 的学生人数,并显示 tutor 姓名:
SELECT l.subjectcode, l.labno, s.fname AS tutorfirstname, s.lname AS tutorlastname, COUNT(ls.studentno) AS totalstudentsFROM lab lJOIN tutor t ON l.tutorno = t.tutornoJOIN student s ON t.studentno = s.studentnoLEFT JOIN lab_signup ls ON l.labno = ls.labnoGROUP BY l.subjectcode, l.labno, s.fname, s.lnameORDER BY l.subjectcode, l.labno;这类查询要先理清表之间的关系:
lab.tutorno → tutor.tutornotutor.studentno → student.studentnolab.labno → lab_signup.labno写复杂查询时,可以按这个顺序构造:
1. 先确定最终要显示哪些字段2. 找出这些字段分别来自哪些表3. 找出表和表之间的连接条件4. 决定使用 INNER JOIN 还是 LEFT JOIN5. 加 WHERE 过滤条件6. 如需统计,再加 GROUP BY7. 最后加 ORDER BY 排序本次 Lab 的核心知识点
这次练习可以归纳为几组能力:
查看 schema 中已有表创建表并设置主键插入完整数据和部分数据处理主键重复错误使用 Oracle 日期函数修改表结构理解 CHAR 和 VARCHAR2 的区别提交事务从已有 schema 复制表使用 JOIN 和 NOT EXISTS 查询关联数据使用聚合函数和 GROUP BY 做统计如果只记一句话:Part A 主要练表结构和数据维护,Part B 主要练多表查询和聚合统计。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!












