知识库✳

教程与手册

全部教程
  1. 01Oracle 函数手册
  2. 02性能优化手册
  3. 03Linux Shell
  4. 04Oracle 学习与演练
  5. 05网络基础学习
  6. 06数据治理与管理
返回个人网站
甲Oracle 学习教程从 SQL 到 PL/SQL 与运维
首页课程目录搜索演练场
知识库

已完成 0 / 22 章

模块一 · 入门与准备

  • 01Oracle 数据库体系结构
  • 02环境安装与连接方式
  • 03SQL*Plus 与常用工具

模块二 · SQL 查询基础

  • 04SELECT 查询基础
  • 05过滤与排序
  • 06单行函数
  • 07多表连接
  • 08分组聚合与 HAVING
  • 09子查询

模块三 · 数据与对象

  • 10DML 与事务控制
  • 11表与数据类型
  • 12约束
  • 13索引与执行计划
  • 14视图、序列与同义词

模块四 · PL/SQL 编程

  • 15PL/SQL 块与变量
  • 16流程控制与游标
  • 17存储过程与函数
  • 18触发器
  • 19异常处理

模块五 · 运维与进阶

  • 20用户与权限管理
  • 21表空间与存储管理
  • 22备份与恢复
❖

Oracle 学习教程

返回个人网站知识库

示例代码基于 Oracle AI Database 26ai / 23ai,查询示例使用官方 HR 示例方案。

以 Next.js 16 · Tailwind CSS v4 · HeroUI v3 装帧

10
  1. 首页
  2. 课程目录
  3. DML 与事务控制

第 10 章◆进阶◆建议 55 分钟◆模块三 · 数据与对象

DML 与事务控制

INSERT/UPDATE/DELETE/MERGE 的写法,以及 COMMIT、ROLLBACK、SAVEPOINT 与锁的行为。

学完这一章,你应该能

  • 熟练编写四类 DML 语句
  • 理解事务边界与 ACID
  • 会用 SAVEPOINT 做部分回滚
  • 知道锁是怎么产生和释放的

10.1四类 DML

INSERT / UPDATE / DELETE
-- 单行插入
INSERT INTO departments (department_id, department_name, location_id)
VALUES (300, 'Research', 1700);

-- 从查询结果批量插入(先建一张结构相同的备用表)
CREATE TABLE dept_backup AS
SELECT department_id, department_name FROM departments WHERE 1 = 0;

INSERT INTO dept_backup (department_id, department_name)
SELECT department_id, department_name FROM departments WHERE location_id = 1700;

-- 条件更新(执行前先用同样的 WHERE 写 SELECT COUNT(*) 确认影响行数)
UPDATE employees SET salary = salary * 1.1 WHERE department_id = 60;

-- 删除
DELETE FROM job_history WHERE employee_id = 176;

只作示例阅读:5 行写入限制下只开放单独创建普通空表;CTAS、删表、清空表、改结构、触发器和存储程序不允许执行。请在独立学习数据库中练习。

MERGE:存在则更新,不存在则插入
-- 先准备好源表与目标表(真实项目里它们通常已经存在)
CREATE TABLE dept_target AS
SELECT department_id, department_name FROM departments;

CREATE TABLE dept_source AS
SELECT department_id, department_name FROM departments WHERE department_id <= 30;

MERGE INTO dept_target t
USING (SELECT department_id, department_name FROM dept_source) s
   ON (t.department_id = s.department_id)
WHEN MATCHED THEN
  UPDATE SET t.department_name = s.department_name
WHEN NOT MATCHED THEN
  INSERT (department_id, department_name)
  VALUES (s.department_id, s.department_name);

MERGE 是数据同步场景的标准写法,比「先 UPDATE 再 INSERT」更安全且只扫一遍源表。

只作示例阅读:5 行写入限制下只开放单独创建普通空表;CTAS、删表、清空表、改结构、触发器和存储程序不允许执行。请在独立学习数据库中练习。

10.2事务与提交

Oracle 没有 BEGIN TRANSACTION。事务在你执行第一条 DML 时自动开始,到 COMMIT 或 ROLLBACK 结束。在此之前,你的修改只有你自己能看到,别人看不到(读一致性)。

  • COMMIT:确认事务,修改永久生效,释放锁
  • ROLLBACK:撤销整个事务,回到事务开始前
  • SAVEPOINT:设置中间点,可以只回滚到某一点
  • DDL 语句(CREATE/ALTER/DROP/TRUNCATE)会隐式提交,且不可回滚
  • AUTOCOMMIT 默认是 OFF,即每条 DML 后不会自动提交,需要你显式 COMMIT
  • 但 EXITCOMMIT 默认是 ON:正常退出 SQL*Plus 时会提交未完成的事务。想避免误提交,要显式 SET EXITCOMMIT OFF
SAVEPOINT 部分回滚
UPDATE employees SET salary = salary + 100 WHERE employee_id = 100;
SAVEPOINT after_first;

UPDATE employees SET salary = salary + 500 WHERE employee_id = 101;

-- 只撤销第二条更新
ROLLBACK TO SAVEPOINT after_first;

-- 此时第一条仍在事务中,确认无误后提交
COMMIT;

只作示例阅读:请使用提交/回滚按钮;不支持自定义保存点或中间提交。请在独立学习数据库中练习。

提示 在生产环境执行 UPDATE/DELETE 之前,先用同样的 WHERE 条件写一条 SELECT COUNT(*) 确认影响行数。这个习惯能避免大部分事故。

10.3锁与并发

  • Oracle 默认行级锁,UPDATE 时自动加,COMMIT/ROLLBACK 时释放
  • 读操作不会被写操作阻塞(多版本读一致性),这是 Oracle 的重要优势
  • 忘记提交会长期占用锁,导致别人被阻塞——这在开发环境很常见
  • SELECT FOR UPDATE 用于「先读后改」的业务逻辑,防止丢失更新
行级锁与 SELECT FOR UPDATE
-- 锁定这两行,直到本事务提交,其他会话无法修改
SELECT employee_id, salary
FROM   employees
WHERE  department_id = 60
FOR    UPDATE NOWAIT;
-- NOWAIT:拿不到锁立即报错,不等待

-- 查看当前阻塞情况
SELECT blocking_session, sid, serial#, wait_class, seconds_in_wait
FROM   v$session
WHERE  blocking_session IS NOT NULL;

只作示例阅读:读取全库字典或会话(沙箱只开放自身对象)。请在独立学习数据库中练习。

练习

3 题 · 先自己写,再展开对照

参考答案

因为 DML 执行后你看到的是「已更新 N 行」,那时已经晚了。

先用相同的 WHERE 条件数一遍影响行数,可以提前发现:
- WHERE 写错或漏写导致条件过宽(影响行数远超预期)
- 条件根本匹配不到任何行(说明逻辑有误)

这个习惯成本极低,能挡掉大部分误更新事故。

自评要点

  • 用 COUNT(*) 预判影响行数
  • 指出条件过宽或过窄两种风险

参考答案

在。

ROLLBACK TO SAVEPOINT 只撤销「该保存点之后」的修改,第一条更新仍然属于
当前事务的一部分。此时既没有提交也没有结束事务,需要再显式 COMMIT
或 ROLLBACK 才会结束。

注意:ROLLBACK TO SAVEPOINT 不会释放已经获得的锁。

自评要点

  • 只回滚保存点之后的修改
  • 事务仍在进行中,必须显式结束

参考答案

被修改的行上持有行级锁,其他会话要改同样的行就会被阻塞,
表现为「卡住不动」,直到你 COMMIT / ROLLBACK(或会话断开)。

排查(DBA 视角):
SELECT blocking_session, sid, serial#, event, seconds_in_wait
FROM   v$session
WHERE  blocking_session IS NOT NULL;

开发环境出现「莫名卡住」,第一件事就是问有没有人没提交。

自评要点

  • 指出未提交会长期持有行锁并阻塞他人
  • 给出 v$session.blocking_session 排查方式
← 上一章

子查询

下一章 →

表与数据类型