数据库基础知识点总结

第一章 SQLPLUS 使用与服务管理

1.1 核心服务启动

Oracle 运行需启动两个核心服务,两种启动方式:

  • 方法 1(图形界面) :控制面板→服务→启动以下服务:

    • OracleServiceORCL(数据库实例服务)
    • OracleTNSListener(监听服务)
  • 方法 2(命令行,管理员权限)

    bash

    运行

    复制代码
    # 启动服务
    net start OracleServiceORCL
    lsnrctl start
    # 停止服务
    net stop OracleServiceORCL
    lsnrctl stop

1.2 SQLPLUS 连接方式

1.2.1 常规连接步骤
  1. 打开 CMD,输入sqlplus进入交互界面;
  2. 输入用户名和口令,格式:用户名/密码 [as 身份](sys 用户必须指定as sysdba)。
1.2.2 常用用户与口令
用户名 角色 默认口令 说明
sys 超级管理员 change_on_install/Oracle11 必须以as sysdba身份登录
system 普通管理员 manager/Oracle11 管理数据库日常操作
scott 普通用户 tiger 10g 后默认锁定,需解锁
sh 测试用户 sh 包含百万级数据的表
1.2.3 scott 用户解锁流程
  1. 以 sys 用户登录: sql

    复制代码
    sqlplus sys/Oracle11 as sysdba
  2. 确认当前用户: sql

    复制代码
    show user; -- 应显示"SYS"
  3. 解锁并重置 scott 密码: sql

    复制代码
    alter user scott identified by tiger account unlock; -- 解锁并设密码为tiger
  4. 切换到 scott 用户: sql

    复制代码
    connect scott/tiger;
    show user; -- 应显示"SCOTT"
1.2.4 一键进入 scott 用户(技巧)

创建批处理文件(scott_login.bat),内容如下:

bash

运行

复制代码
@echo off
:: 启动核心服务
net start OracleServiceORCL
lsnrctl start
:: 连接scott用户(直接输入密码,避免交互)
sqlplus scott/tiger

双击运行即可一键登录(需确保服务未启动时使用)。

第二章 SQL 基础

2.1 SQL 分类

分类 英文缩写 功能 常用命令
数据查询 DQL 查询表数据 select
数据定义 DDL 创建 / 修改 / 删除数据库对象 create, alter, drop
数据修改 DML 增删改表数据 insert, update, delete
数据控制 DCL 权限管理与事务控制 grant, revoke, commit, rollback

2.2 基础查询操作

2.2.1 查看用户表

sql

复制代码
select * from tab; -- 显示当前用户所有表(tab是系统伪表)
2.2.2 查看表结构

sql

复制代码
desc 表名; -- 例:desc emp; 显示emp表的列名、类型、约束
2.2.3 基本查询语法

sql

复制代码
select [列名1, 列名2, ... | *] -- *表示查询所有列
from 表名
[where 条件] -- 筛选数据
[group by 分组列] -- 分组统计
[having 分组条件] -- 筛选分组结果
[order by 排序列 [asc|desc]]; -- 排序(asc升序,默认;desc降序)
2.2.4 常用查询技巧
  1. 查询所有列

    sql

    复制代码
    select * from emp; -- 查询emp表所有数据
  2. 查询特定列

    sql

    复制代码
    select ename, sal, hiredate from emp; -- 查询员工姓名、工资、入职日期
  3. 去重查询distinct):

    sql

    复制代码
    select distinct deptno from emp; -- 查询不重复的部门号
  4. 字符串连接||):

    sql

    复制代码
    select ename || '的工资是:' || sal as 工资信息 from emp; -- 自定义输出格式

第三章 条件查询与排序

3.1 条件查询(where 子句)

3.1.1 比较运算符
运算符 含义 示例
= 等于 deptno = 10
!=/<> 不等于 sal != 5000
>/< 大于 / 小于 hiredate > '01-1 月 - 1982'
>=/<= 大于等于 / 小于等于 sal >= 3000
3.1.2 逻辑运算符(优先级:not > and > or)

sql

复制代码
-- 示例:查询10号部门的经理或20号部门的职员
select * from emp where (deptno=10 and job='MANAGER') or (deptno=20 and job='CLERK');
3.1.3 模糊查询(like)
  • _:匹配任意 1 个字符;
  • %:匹配任意 0 个或多个字符。

sql

复制代码
-- 示例1:查询姓名包含'A'的员工
select * from emp where ename like '%A%';
-- 示例2:查询姓名第2个字符是'A'的员工
select * from emp where ename like '_A%';
3.1.4 范围查询(in /between and)
  • in:匹配列表中的任意一个值;
  • between and:匹配连续范围内的值(闭区间)。

sql

复制代码
-- 示例1:查询20、30号部门的员工
select * from emp where deptno in (20, 30);
-- 示例2:查询工资在2000-3000之间的员工
select * from emp where sal between 2000 and 3000; -- 等价于sal>=2000 and sal<=3000
3.1.5 空值处理(is null /is not null)

空值(null)不等于任何值,需用专用运算符判断:

sql

复制代码
-- 示例:查询没有奖金的员工(comm为空)
select * from emp where comm is null;
-- 示例:查询有奖金的员工
select * from emp where comm is not null;

3.2 排序(order by)

sql

复制代码
-- 示例1:按工资升序排列(默认asc)
select * from emp order by sal;
-- 示例2:按工资降序排列,工资相同按入职日期升序
select * from emp order by sal desc, hiredate asc;

第四章 函数

4.1 单行函数

4.1.1 字符串函数
函数 功能 示例
upper(str) 转换为大写 upper(ename) → 'SMITH'
lower(str) 转换为小写 lower(ename) → 'smith'
initcap(str) 首字母大写,其余小写 initcap(ename) → 'Smith'
instr(str1, str2,m,n) 从 str1 的第 m 位开始,找 str2 第 n 次出现的位置 instr('Oracle', 'a', 1, 1) → 2
substr(str, m,n) 截取 str 从第 m 位开始的 n 个字符(n 省略则取到末尾) substr('Oracle', 2, 3) → 'rac'
4.1.2 日期函数
函数 功能 示例
sysdate 获取当前系统日期时间 select sysdate from dual;
months_between(d1, d2) 计算 d1 与 d2 相差的月数 months_between (sysdate, hiredate)/12 → 工龄(年)
to_char (d, ' 格式 ') 日期转字符串 to_char(sysdate, 'yyyy-mm-dd hh:mi:ss') → '2024-05-20 14:30:00'
to_date (str, ' 格式 ') 字符串转日期 to_date ('2024-05-20', 'yyyy-mm-dd') → 日期类型
4.1.3 数值函数
函数 功能 示例
round(n, m) 四舍五入(m 为小数位数) round(3.1415, 2) → 3.14
trunc(n, m) 截断(不四舍五入) trunc(3.1415, 2) → 3.14
mod(n, m) 求余 mod(10, 3) → 1
floor(n) 向下取整 floor(3.9) → 3
ceil(n) 向上取整 ceil(3.1) → 4
4.1.4 空值处理函数
函数 功能 示例
nvl(a, b) a 为空则返回 b,否则返回 a nvl (comm, 0) → 奖金为空时按 0 计算
nvl2(a, b, c) a 不为空返回 b,为空返回 c nvl2 (comm, sal+comm, sal) → 有奖金则加奖金

4.2 分组函数(多行函数)

4.2.1 常用分组函数
函数 功能 示例
count(*) 统计总行数(包含空值) select count (*) from emp; → 员工总数
count (列名) 统计该列非空值的行数 select count (comm) from emp; → 有奖金的员工数
sum (列名) 求和(仅数值型) select sum (sal) from emp; → 工资总和
avg (列名) 求平均值(仅数值型) select avg (sal) from emp; → 平均工资
max (列名) 求最大值 select max (sal) from emp; → 最高工资
min (列名) 求最小值 select min (sal) from emp; → 最低工资
4.2.2 分组函数使用注意事项
  1. 分组函数忽略空值(count(*)除外);
  2. sumavg仅支持数值型列;
  3. 若查询中同时包含普通列和分组函数,普通列必须放在group by子句中;
  4. 分组后的筛选用having子句(不能用wherewhere在分组前筛选)。
4.2.3 示例:分组查询

sql

复制代码
-- 示例1:按部门分组,统计每个部门的员工数和平均工资
select deptno, count(empno) as 员工数, avg(sal) as 平均工资
from emp
group by deptno;

-- 示例2:查询员工数超过3人的部门
select deptno, count(empno) as 员工数
from emp
group by deptno
having count(empno) > 3; -- having筛选分组结果

第五章 多表查询

5.1 多表查询分类

连接类型 功能 语法特点
内连接 仅返回满足连接条件的记录 where指定连接条件(等值 / 不等值)
外连接 返回满足条件的记录 + 部分不满足条件的记录 (+)运算符(Oracle 专属)或 SQL99 语法
自连接 同一表内的连接(模拟多表) 表别名区分不同 "角色"

5.2 内连接

5.2.1 等值连接(最常用)

条件:两表有公共列(数据类型一致),连接条件为公共列相等。

sql

复制代码
-- 示例:查询每个员工的工号、姓名、部门名(emp表和dept表连接)
select e.empno, e.ename, d.dname
from emp e, dept d -- e和d是表别名(简化书写)
where e.deptno = d.deptno; -- 连接条件(公共列deptno)
5.2.2 不等连接

条件:两表无公共列,连接条件为非等值比较。

sql

复制代码
-- 示例:查询每个员工的工资等级(emp表和salgrade表连接)
select e.ename, e.sal, s.grade
from emp e, salgrade s
where e.sal between s.losal and s.hisal; -- 不等连接条件(工资在等级范围内)
5.2.3 自连接

条件:同一表中不同行之间的连接(需用别名区分)。

sql

复制代码
-- 示例:查询每个员工的姓名及其经理的姓名(emp表自连接)
select e.ename as 员工姓名, m.ename as 经理姓名
from emp e, emp m -- e:员工表;m:经理表
where e.mgr = m.empno; -- 连接条件(员工的经理号=经理的工号)

5.3 外连接

外连接会保留其中一个表的所有记录,另一个表不满足条件的记录用null填充。Oracle 用(+)表示 "信息缺失的一方"。

5.3.1 左外连接(保留左表所有记录)

sql

复制代码
-- 示例:查询所有员工的姓名及其经理姓名(包括无经理的员工)
select e.ename as 员工姓名, m.ename as 经理姓名
from emp e, emp m
where e.mgr = m.empno(+); -- m表是信息缺失方(无经理时m.ename为null)
5.3.2 右外连接(保留右表所有记录)

sql

复制代码
-- 示例:查询所有部门的名称及其员工数(包括无员工的部门)
select d.dname as 部门名, count(e.empno) as 员工数
from emp e, dept d
where e.deptno(+) = d.deptno -- e表是信息缺失方(无员工时count为0)
group by d.dname;

5.4 SQL99 连接语法(标准语法)

sql

复制代码
-- 内连接
select e.ename, d.dname
from emp e
inner join dept d on e.deptno = d.deptno;

-- 左外连接
select e.ename, m.ename
from emp e
left outer join emp m on e.mgr = m.empno;

-- 右外连接
select d.dname, count(e.empno)
from emp e
right outer join dept d on e.deptno = d.deptno
group by d.dname;

第六章 子查询

6.1 子查询分类

分类 功能 特点
单行子查询 返回单列单值 可用=><等简单比较运算符
多行子查询 返回单列多值 需用inanyall等多行运算符
多列子查询 返回多列多值 用于复杂条件匹配
相关子查询 子查询依赖父查询的结果 逐行执行,效率较低

6.2 单行子查询

sql

复制代码
-- 示例:查询比7788号员工工资高的员工信息
select * from emp
where sal > (select sal from emp where empno = 7788); -- 子查询返回7788的工资(单值)

6.3 多行子查询

6.3.1 in运算符(匹配列表中的任意值)

sql

复制代码
-- 示例:查询20号或30号部门的员工信息
select * from emp
where deptno in (select deptno from dept where dname in ('RESEARCH', 'SALES'));
6.3.2 any/all运算符(与比较运算符结合)
  • any:匹配列表中任意一个值(等价于in);
  • all:匹配列表中所有值。

sql

复制代码
-- 示例1:查询比20号部门任意员工工资高的员工(> any 等价于 > 最小值)
select * from emp
where sal > any (select sal from emp where deptno = 20);

-- 示例2:查询比20号部门所有员工工资高的员工(> all 等价于 > 最大值)
select * from emp
where sal > all (select sal from emp where deptno = 20);

6.4 子查询应用:获取特定行记录

利用rownum(伪列,行号)和子查询获取第 N 行或指定范围的记录:

sql

复制代码
-- 示例1:获取emp表第4、6、8、10行记录
select * from (select rownum as rn, emp.* from emp) a
where a.rn in (4, 6, 8, 10);

-- 示例2:获取emp表第5-10行记录
select * from (select rownum as rn, emp.* from emp) a
where a.rn between 5 and 10;

第七章 DDL 与表约束

7.1 表的创建

7.1.1 直接创建表

sql

复制代码
-- 示例:创建学生表student(学号、姓名、性别、出生日期、入学时间)
create table student (
    s_no char(11) primary key, -- 学号(主键,11位)
    s_name varchar2(20) not null, -- 姓名(非空)
    s_gender char(1) check (s_gender in ('F', 'M')), -- 性别(只能是F/M)
    s_birth date not null, -- 出生日期(非空)
    s_come date -- 入学时间(可空)
);
7.1.2 基于查询创建表(复制表结构 + 数据)

sql

复制代码
-- 示例1:复制dept表到dept_bak(含数据)
create table dept_bak as select * from dept;

-- 示例2:复制emp表的部门号和平均工资到t_avgsal(仅结构+统计数据)
create table t_avgsal as 
select deptno, avg(sal) as avgsal from emp group by deptno;

7.2 表的约束

约束用于保证数据完整性,常用约束类型如下:

约束类型 关键字 功能 示例
主键约束 primary key 唯一标识行,非空且唯一 s_no char(11) primary key
唯一约束 unique 列值唯一,可空 s_name varchar2(20) unique
非空约束 not null 列值不能为空 s_birth date not null
检查约束 check 列值满足指定条件 s_gender check (s_gender in ('F','M'))
外键约束 foreign key 引用其他表的主键 / 唯一键 s_no char(11) references student(s_no)
7.2.1 约束的维护

sql

复制代码
-- 1. 添加约束(表级约束)
alter table score add constraint fk_s_no foreign key (s_no) references student(s_no);

-- 2. 删除约束
alter table score drop constraint fk_s_no;

-- 3. 禁用约束(临时失效)
alter table score disable constraint fk_s_no;

-- 4. 启用约束(恢复生效)
alter table score enable constraint fk_s_no;
7.2.2 查看约束

sql

复制代码
-- 查看当前用户所有表的约束
select constraint_name, constraint_type, table_name 
from user_constraints;

-- 查看student表的约束
select constraint_name, constraint_type 
from user_constraints 
where table_name = 'STUDENT';

7.3 表结构的修改

sql

复制代码
-- 1. 添加列
alter table student add (s_address varchar2(50)); -- 新增地址列

-- 2. 修改列属性(类型/长度,需注意数据兼容性)
alter table student modify s_address char(100); -- 地址列改为char(100)

-- 3. 删除列
alter table student drop column s_address; -- 删除地址列

-- 4. 修改表名
rename student to stu; -- 表名改为stu

第八章 视图、索引与序列号

8.1 视图

视图是基于表的 "虚表",不存储数据,仅保存查询逻辑,用于简化复杂查询和控制数据访问。

8.1.1 创建视图

sql

复制代码
-- 示例1:创建视图view_emp_dept(员工姓名、部门名、工资)
create view view_emp_dept as
select e.ename, d.dname, e.sal
from emp e, dept d
where e.deptno = d.deptno;

-- 示例2:创建只读视图(禁止DML操作)
create view view_dept as
select * from dept with read only;

-- 示例3:创建带check option的视图(修改数据需满足where条件)
create view view_emp_20 as
select * from emp where deptno = 20 with check option; -- 只能修改20号部门的员工
8.1.2 视图的使用

sql

复制代码
-- 查询视图(与查询表一致)
select * from view_emp_dept where sal > 3000;

-- 修改视图(仅非只读、带check option且满足条件时可用)
update view_emp_20 set sal = 3500 where ename = 'SMITH';

8.2 索引

索引用于提升查询速度,通过建立 "索引树" 减少全表扫描,但会占用存储空间,且 DML 操作时需维护索引(降低写入效率)。

8.2.1 创建索引

sql

复制代码
-- 示例1:创建单列索引(emp表的ename列)
create index idx_emp_ename on emp(ename);

-- 示例2:创建复合索引(emp表的deptno+sal列,按deptno升序、sal降序)
create index idx_emp_deptno_sal on emp(deptno, sal desc);

-- 示例3:创建基于函数的索引(解决模糊查询效率问题)
create index idx_emp_lower_ename on emp(lower(ename)); -- 用于lower(ename)的查询
8.2.2 索引的使用与维护

sql

复制代码
-- 查看索引(当前用户所有索引)
select index_name, table_name, column_name 
from user_ind_columns 
where table_name = 'EMP';

-- 重建索引(索引碎片化后提升性能)
alter index idx_emp_ename rebuild;

-- 删除索引
drop index idx_emp_ename;
8.2.3 索引使用建议
  1. 表数据量大(万级以上)时创建索引;
  2. 列值重复率低(如主键、唯一列)适合建索引;
  3. 频繁查询的列(where、join、order by 中的列)建索引;
  4. 避免在频繁更新的列上建索引(维护成本高)。

8.3 序列号

序列号用于生成唯一的自增数字,常用于主键列的自动赋值。

8.3.1 创建序列号

sql

复制代码
-- 示例:创建序列号seq_emp,从1开始,每次增1,无最大值
create sequence seq_emp
start with 1 -- 起始值
increment by 1 -- 增量
nomaxvalue -- 无最大值
nominvalue -- 无最小值
cache 20; -- 缓存20个值(提升性能)
8.3.2 序列号的使用

序列号的两个核心伪列:

  • nextval:获取下一个序列号(首次使用需用 nextval 初始化);
  • currval:获取当前序列号(需先调用 nextval)。

sql

复制代码
-- 示例:用序列号给emp表插入新员工(主键empno自动生成)
insert into emp(empno, ename, job, deptno)
values(seq_emp.nextval, 'LISI', 'CLERK', 20); -- nextval生成下一个序列号

-- 查看当前序列号
select seq_emp.currval from dual; -- 返回刚插入的empno

第九章 PL/SQL 基础

PL/SQL(Procedural Language/SQL)是 Oracle 的过程化编程语言,结合了 SQL 的查询能力和编程语言的流程控制(条件、循环等)。

9.1 PL/SQL 块结构

PL/SQL 程序由 "块" 组成,每个块包含 3 个部分(声明部分和异常处理部分可选):

sql

复制代码
declare
    -- 声明部分:定义变量、常量、游标等(可选)
    变量名 数据类型 [:= 初值];
begin
    -- 执行部分:核心逻辑(必须)
    SQL语句;
    PL/SQL语句;
exception
    -- 异常处理部分:处理执行过程中的错误(可选)
    when 异常类型 then 处理逻辑;
end;
/ -- 结束符(必须)

9.2 变量声明与赋值

9.2.1 普通变量

sql

复制代码
-- 示例:输出"Hello PL/SQL"
set serveroutput on; -- 开启屏幕打印(必须)
declare
    v_msg varchar2(20) := 'Hello PL/SQL'; -- 声明变量并赋值
begin
    dbms_output.put_line(v_msg); -- 打印变量
end;
/
9.2.2 引用型变量(%type

引用已有表列的数据类型,避免硬编码:

sql

复制代码
-- 示例:查询7788号员工的姓名和工资
declare
    v_ename emp.ename%type; -- 引用emp表ename列的类型
    v_sal emp.sal%type; -- 引用emp表sal列的类型
begin
    select ename, sal into v_ename, v_sal from emp where empno = 7788;
    dbms_output.put_line('姓名:' || v_ename || ',工资:' || v_sal);
end;
/
9.2.3 记录型变量(%rowtype

引用整个表的行结构,用于存储一行数据:

sql

复制代码
-- 示例:查询30号部门的所有信息
declare
    v_dept dept%rowtype; -- 引用dept表的行结构
begin
    select * into v_dept from dept where deptno = 30;
    dbms_output.put_line('部门号:' || v_dept.deptno || ',部门名:' || v_dept.dname);
end;
/

9.3 流程控制

9.3.1 条件判断(if 语句)

sql

复制代码
-- 示例:根据工资判断员工等级
declare
    v_sal emp.sal%type;
begin
    select sal into v_sal from emp where empno = 7788;
    if v_sal > 3000 then
        dbms_output.put_line('高工资');
    elsif v_sal between 1500 and 3000 then
        dbms_output.put_line('中等工资');
    else
        dbms_output.put_line('低工资');
    end if;
end;
/
9.3.2 循环(loop/while/for)

sql

复制代码
-- 示例1:loop循环(计算1+2+...+100)
declare
    v_i number := 1;
    v_sum number := 0;
begin
    loop
        v_sum := v_sum + v_i;
        v_i := v_i + 1;
        exit when v_i > 100; -- 退出条件
    end loop;
    dbms_output.put_line('1+2+...+100=' || v_sum);
end;
/

-- 示例2:for循环(计算1-100的偶数和)
declare
    v_sum number := 0;
begin
    for v_i in 2..100 loop -- 步长1,2到100
        if mod(v_i, 2) = 0 then -- 判断偶数
            v_sum := v_sum + v_i;
        end if;
    end loop;
    dbms_output.put_line('1-100偶数和=' || v_sum);
end;
/

第十章 游标

游标用于临时存储查询结果集,解决 "select into" 只能返回单行的问题,分为隐式游标和显式游标。

10.1 隐式游标

系统自动创建的游标,用于执行select into、DML(insert/update/delete)语句,游标名为sql

10.1.1 隐式游标属性
属性 功能 示例
%found 操作成功(如查询到数据、DML 影响行数 > 0)返回 true if sql%found then commit; end if;
%notfound 操作失败返回 true if sql%notfound then rollback; end if;
%rowcount DML 操作影响的行数 dbms_output.put_line (' 更新行数:' sql%rowcount);
%isopen 游标是否打开(隐式游标始终为 false) 无实际使用场景
10.1.2 示例:隐式游标判断更新是否成功

sql

复制代码
begin
    update emp set sal = 3500 where empno = 1234; -- 假设1234号员工不存在
    if sql%found then
        dbms_output.put_line('更新成功,影响行数:' || sql%rowcount);
        commit;
    else
        dbms_output.put_line('更新失败,未找到员工');
        rollback;
    end if;
end;
/

10.2 显式游标

用户自定义的游标,用于处理多行查询结果,需手动完成 "声明→打开→提取→关闭" 四步。

10.2.1 显式游标使用步骤

sql

复制代码
-- 示例:查询20号部门的所有员工姓名和入职日期
declare
    -- 1. 声明游标(定义查询逻辑)
    cursor cur_emp is
        select ename, hiredate from emp where deptno = 20;
    -- 声明变量存储提取的数据(与游标查询列对应)
    v_ename emp.ename%type;
    v_hiredate emp.hiredate%type;
begin
    -- 2. 打开游标(执行查询,加载结果集到内存)
    open cur_emp;
    -- 3. 提取数据(循环提取所有行)
    loop
        fetch cur_emp into v_ename, v_hiredate; -- 提取一行到变量
        exit when cur_emp%notfound; -- 无数据时退出循环
        -- 处理数据
        dbms_output.put_line('姓名:' || v_ename || ',入职日期:' || to_char(v_hiredate, 'yyyy-mm-dd'));
    end loop;
    -- 4. 关闭游标(释放内存)
    close cur_emp;
end;
/
10.2.2 显式游标简化(for 循环)

for 循环会自动完成 "打开→提取→关闭",无需手动操作:

sql

复制代码
-- 示例:用for循环查询20号部门员工
declare
    cursor cur_emp is
        select ename, hiredate from emp where deptno = 20;
begin
    for rec_emp in cur_emp loop -- rec_emp是记录型变量,自动匹配游标列
        dbms_output.put_line('姓名:' || rec_emp.ename || ',入职日期:' || to_char(rec_emp.hiredate, 'yyyy-mm-dd'));
    end loop;
end;
/
10.2.3 带参数的游标

游标可接收参数,动态调整查询条件:

sql

复制代码
-- 示例:查询指定部门的员工(参数为部门号)
declare
    cursor cur_emp(p_deptno number) is -- p_deptno是参数
        select ename, sal from emp where deptno = p_deptno;
begin
    for rec_emp in cur_emp(30) loop -- 传入参数30(查询30号部门)
        dbms_output.put_line('姓名:' || rec_emp.ename || ',工资:' || rec_emp.sal);
    end loop;
end;
/

第十一章 触发器

触发器是一种特殊的 PL/SQL 程序,在满足 "特定事件" 时自动执行,用于实现复杂的数据完整性、同步备份、日志记录等功能。

11.1 触发器分类

分类 触发事件 作用对象 示例场景
DML 触发器 insert/update/delete 限制工资修改幅度、级联更新
替代触发器 insert/update/delete 视图 实现视图的 DML 操作
DDL 触发器 create/alter/drop 模式(用户) 禁止删除表、记录 DDL 操作日志
数据库触发器 数据库启动 / 关闭、用户登录 / 退出 数据库 数据库启动时自动执行备份

11.2 DML 触发器(最常用)

11.2.1 核心概念
  • 触发时间before(事件前执行)或after(事件后执行);
  • 触发事件insertupdatedelete(可组合);
  • 触发级别for each row(行级触发器,每影响一行执行一次)或for each statement(语句级触发器,整个语句执行一次);
  • 谓词inserting(是否执行 insert)、updating(是否执行 update)、deleting(是否执行 delete);
  • 伪记录 :old(操作前的数据)、 :new(操作后的数据),仅行级触发器可用。
11.2.2 示例 1:限制工资修改幅度(行级触发器)

sql

复制代码
-- 需求:员工工资修改幅度不能超过300
create or replace trigger tri_emp_sal_check
before update of sal -- 触发时间:更新前;触发事件:更新sal列
on emp
for each row -- 行级触发器
begin
    -- 判断修改幅度是否超过300(abs取绝对值)
    if abs(:new.sal - :old.sal) > 300 then
        -- 抛出自定义异常(-20000~-20999为用户自定义异常码)
        raise_application_error(-20001, '工资修改幅度不能超过300!');
    end if;
end;
/

-- 测试:修改7788号员工工资(幅度300以内,成功)
update emp set sal = 3300 where empno = 7788; -- 原工资3000

-- 测试:修改幅度400(失败,触发异常)
update emp set sal = 3400 where empno = 7788;
11.2.3 示例 2:级联更新(行级触发器)

sql

复制代码
-- 需求:当dept表的deptno修改时,自动更新emp表对应的deptno(解决外键约束限制)
create or replace trigger tri_deptno_cascade
after update of deptno -- 触发时间:更新后;触发事件:更新deptno列
on dept
for each row -- 行级触发器
begin
    -- 级联更新emp表的deptno
    update emp set deptno = :new.deptno where deptno = :old.deptno;
end;
/

-- 测试:将10号部门改为11号(emp表中10号部门的员工会自动改为11号)
update dept set deptno = 11 where deptno = 10;
11.2.4 示例 3:同步备份(行级触发器)

sql

复制代码
-- 需求:dept表的insert/update/delete操作自动同步到dept_bak表
-- 1. 先创建备份表dept_bak
create table dept_bak as select * from dept;

-- 2. 创建触发器
create or replace trigger tri_dept_bak
after insert or update or delete -- 触发事件:insert/update/delete
on dept
for each row -- 行级触发器
begin
    if inserting then
        -- 插入操作:同步插入到dept_bak
        insert into dept_bak values(:new.deptno, :new.dname, :new.loc);
    elsif updating then
        -- 更新操作:同步更新dept_bak
        update dept_bak set dname = :new.dname, loc = :new.loc where deptno = :old.deptno;
    else
        -- 删除操作:同步删除dept_bak
        delete from dept_bak where deptno = :old.deptno;
    end if;
end;
/

-- 测试:插入新部门(dept_bak会自动同步)
insert into dept(deptno, dname, loc) values(50, 'FINANCE', 'CHICAGO');

-- 测试:更新部门名称(dept_bak会自动同步)
update dept set dname = 'FINANCE_DEPT' where deptno = 50;

-- 测试:删除部门(dept_bak会自动同步)
delete from dept where deptno = 50;

第十二章 实验技巧与常见问题

12.1 实验过程记录(spool 命令)

sql

复制代码
-- 开启spool(记录到D盘的oracle_log.txt,append表示追加,不覆盖)
spool d:/oracle_log.txt append

-- 执行实验操作(示例)
select * from emp;
update emp set sal = 3500 where ename = 'SCOTT';
commit;

-- 关闭spool(必须执行,否则文件不完整)
spool off

12.2 SQL 错误快速修改(edit 命令)

sql

复制代码
-- 执行错误SQL后,输入edit编辑
edit

-- 修改完成后保存退出(记事本/vi),输入/重新执行
/

12.3 常见问题排查

12.3.1 连接失败
  • 检查OracleServiceORCLOracleTNSListener服务是否启动;
  • 检查用户名 / 密码是否正确(sys 需加as sysdba);
  • 检查监听配置(tnsnames.ora文件是否正确)。
12.3.2 权限不足
  • 普通用户(如 scott)无创建视图 / 索引权限,需 sys 授权: sql

    复制代码
    grant create view, create index to scott;
12.3.3 约束冲突
  • 外键约束:删除 / 修改主表数据时,需先处理子表关联数据(或用级联触发器);
  • 唯一约束:确保插入 / 更新的列值不重复;
  • 非空约束:确保必填列有值。
12.3.4 游标未关闭
  • 显式游标需手动关闭(或用 for 循环自动关闭),否则会占用内存;
  • 检查游标是否在异常处理中也关闭(避免异常导致未关闭)。
相关推荐
z1234567898610 分钟前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
程序猿DD12 分钟前
一个 API Key,统一调用大模型、生图和联网搜索
数据库·网关
TlSfoward1 小时前
抓包代理链路下的 TLS 指纹变化分析 TLSFOWARD抓包工具
数据库·爬虫·网络协议·搜索引擎·php
数智化管理手记1 小时前
账龄分析手工统计易遗漏?自动账龄分析工具怎么搭建
大数据·网络·数据库·人工智能·数据挖掘
LabVIEW开发2 小时前
NI Package Manager安装LabVIEW时InternalServerError错误
网络·数据库·labview·labview知识·labview功能·labview程序
SelectDB2 小时前
PostgreSQL 实时同步到 Apache Doris:一站式 CDC 解决方案
数据库
Database_Cool_2 小时前
AI Agent 长期记忆怎么存?阿里云 PolarDB-X 对接 Mem0 记忆框架实践
数据库·阿里云
会编程的土豆3 小时前
数据库范式
数据库·oracle
L1624763 小时前
MySQL 全环境生产快速安装 + 完整配置手册(整合国产银河麒麟适配 + 主从补充版)
数据库·mysql
yio_yin3 小时前
Spring事务管理(数据一致性)
数据库·sql·spring