前情提要:本篇博客将详细介绍DML语言语法规则,并且有详细示例演示使用DML语言管理表,同时还有详细解析
oracle版本:19c
当你执行DML语句时:
-
向表中添加新行
-
修改表中的现有行
-
从表中删除现有行
事务由构成逻辑工作单元的DML语句的集合组成。
一、INSERT语句
向表添加新行

1.1 INSERT语句语法
通过使用INSERT 语句将新行添加到表中:
sql
INSERT INTO table [(column [, column...])]
VALUES (value [, value...]);
-- 使用这种语法,一次只能插入一行
1.2 插入新行
注意事项:
-
插入一个新行,其中包含每一列的值。
-
以表中列的默认顺序列出值并对应插入。
-
(可选)列出INSERT子句中的列。
-
将字符和日期值括在单引号内。
示例
sql
-- 查看departments表结构
SQL> DESC departments;
Name Null? Type
----------------------------------------- -------- ----------------------------
DEPARTMENT_ID NOT NULL NUMBER(4)
DEPARTMENT_NAME NOT NULL VARCHAR2(30)
MANAGER_ID NUMBER(6)
LOCATION_ID NUMBER(4)
-- 向departments表插入一行数据
-- 显示写法,列出所有列的列名
SQL> INSERT INTO departments(department_id, department_name, manager_id, location_id) VALUES (71, 'Public Relations', 100, 1700);
1 row created.
-- 隐式写法,如果插入的是完整的一行数据则可以省略列名
INSERT INTO departments VALUES (71, 'Public Relations', 100, 1700);
-- 在ORACLE数据库中,插入数据后要使用commit语句进行事务的提交,而在MYSQL数据库中,则不需要commit因为,MYSQL数据库默认的是自动提交,而ORACLE数据库默认不自动提交
-- 在ORACLE数据库中,插入数据后发现数据有问题或者有错误的时候,我们可以使用ROLLBACK语句回滚事务使刚刚的插入失效,但是有个前提是这条语句还没有提交生效
-- 并且无论是在ORACLE数据库还是在MYSQL数据库插入数据时,都需要去关注,数据库表中该列是否有约束条件,比如说主键约束,唯一键约束,非空约束等
ROLLBACK; -- 撤销刚才的插入
COMMIT; -- 提交刚才的插入,使插入生效
1.3 插入具有空行的值
- 隐式方法: 从列表中省略该列
sql
INSERT INTO departments (department_id, department_name) VALUES (30, 'Purchasing');
- 显示方法:在VALUES子句中指定NULL关键字
sql
INSERT INTO departments VALUES (100, 'Finance', NULL, NULL);
示例
sql
-- 创建示例表
SQL> CREATE TABLE test(ID1 NUMBER,ID2 NUMBER);
-- 插入一行数据
SQL> INSERT INTO test VALUES (1,1);
SQL> SELECT * FROM test;
ID1 ID2
---------- ----------
1 1
-- 隐式插入空值到ID2列
SQL> INSERT INTO test VALUES (1,NULL);
-- 隐式插入空值到ID1列
SQL> INSERT INTO test VALUES (NULL,2);
-- 查看表的数据,可见ID2的第二行数据和ID1的第三行数据为NULL
SQL> SELECT * FROM test;
ID1 ID2
---------- ----------
1 1
1
2
-- 显示插入空值到ID2列,指定要插入的列
SQL> INSERT INTO test(ID2) VALUES(3);
-- 查看数据
SQL> SELECT * FROM test;
ID1 ID2
---------- ----------
1 1
1
2
3
-- 回滚事务使插入失效
ROLLBACK
-- 提交事务使插入生效
COMMIT
1.4 插入特殊值
在VALUES子句中除了用户自己指定值以外还可以使用函数来产生结果作为列的值进行插入
示例
- 使用CURRENT_DATE函数插入系统时间
sql
-- 假如说新入职了一个员工,那么可以在VALUES子句中使用CURRENT_DATE函数直接插入当前时间
INSERT INTO employees (employee_id, first_name, last_name, email, phone_number,hire_date, job_id, salary, commission_pct, manager_id,department_id)
VALUES (113, 'Louis', 'Popp', 'LPOPP', '515.124.4567', CURRENT_DATE, 'AC_ACCOUNT', 6900, NULL, 205, 110);
- 使用TO_DATE函数插入指定时间
sql
INSERT INTO employees VALUES
(114,
'Den', 'Raphealy',
'DRAPHEAL', '515.127.4561',
TO_DATE('FEB 3, 2003', 'MON DD, YYYY'),
'SA_REP', 11000, 0.2, 100, 60);
可以使用替代变量作为VALUES子句的值,让用户自己输入想要插入的内容
示例
sql
-- 查看测试表的结构
SQL> DESC TEST;
Name Null? Type
----------------------------------------- -------- ----------------------------
ID1 NUMBER
ID2 NUMBER
-- 使用替代变量作为VALUES的值
SQL> SELECT * FROM TEST;
no rows selected
SQL> INSERT INTO TEST VALUES(&ID1,&ID2);
Enter value for ID1: 11 -- 指定ID1为11
Enter value for ID2: 22 -- 指定ID2为22
old 1: INSERT INTO TEST VALUES(&ID1,&ID2)
new 1: INSERT INTO TEST VALUES(11,22)
1 row created.
-- 查看TEST表
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
11 22
1.5 从另一个表复制行插入
-
用子查询编写INSERT语句:
-
不要使用VALUES子句。
-
要保证INSERT子句中的列数与子查询中的列数匹配。
-
要保证插入的表的列的数据类型和子查询的结果的列的数据类型匹配
-
将子查询返回的所有行插入表中
示例
sql
-- 创建示例表TEST1
SQL> CREATE TABLE TEST1(ID1 NUMBER,ID2 NUMBER);
-- 给示例表插入数据并提交事务
SQL> INSERT INTO TEST1 VALUES(1,1);
SQL> INSERT INTO TEST1 VALUES(2,2);
SQL> COMMIT;
-- 通过子查询查TEST1然后将结果插入到TEST中
-- 查询TEST1表中ID1=1的行插入到TEST表中
SQL> INSERT INTO TEST SELECT * FROM TEST1 WHERE ID1=1;
1 row created.
-- 查看TEST表
SQL> INSERT INTO TEST SELECT * FROM TEST1 WHERE ID1=1;
1 row created.
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
11 22
1 1
-- 提交事务
SQL> COMMIT;
Commit complete.
二、UPDATE语句
修改表中的数据

2.1 UPDATE语句的语法
使用UPDATE语句修改表中存在的数据:
sql
UPDATE table
SET column = value [, column = value, ...]
[WHERE condition];
注意:UPDATE语句一定要搭配WHERE条件限制来使用,这样才能保证修改点精确,如果不使用WHERE条件限制则UPDATE会默认修改整个表的行
2.2 修改表中的行
如果指定WHERE子句,则将修改一个或多个特定行的值
示例
- 使用WHERE子句限制修改单行内容
sql
-- 查看TEST表的内容
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
11 22
1 1
-- 为TEST表添加一些内容
SQL> INSERT INTO TEST VALUES(2,2);
SQL> INSERT INTO TEST VALUES(3,4);
SQL> INSERT INTO TEST VALUES(7,8);
SQL> INSERT INTO TEST VALUES(6,6);
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
11 22
1 1
2 2
3 4
7 8
6 6
SQL> COMMIT;
-- 使用UPDATE更新一行内容
-- 更新ID1=11的行的ID1列的值为123
SQL> UPDATE TEST SET ID1 = 123 WHERE ID1 = 11;
1 row updated.
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
123 22
1 1
2 2
3 4
7 8
6 6
6 rows selected.
-- 提交事务
SQL> COMMIT;
Commit complete.
- 使用WHERE限制修改多行的内容
sql
-- 使用UPDATE更新多行内容
-- 更新ID1=6或者ID2=8的行的ID1列的值为666
SQL> UPDATE TEST SET ID1=666 WHERE ID1=6 OR ID2=8;
2 rows updated.
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
123 22
1 1
2 2
3 4
666 8
666 6
6 rows selected.
-- 提交事务
SQL> COMMIT;
Commit complete.
如果省略WHERE子句,则会修改表中所有行的值:
示例:
sql
SQL> UPDATE TEST SET ID1=00;
6 rows updated.
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
0 22
0 1
0 2
0 4
0 8
0 6
6 rows selected.
-- 回滚事务
SQL> ROLLBACK;
Rollback complete.
指定SET column_name = NULL将列值更新为NULL.
示例:
sql
SQL> UPDATE TEST SET ID1=NULL WHERE ID2=22;
1 row updated.
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
22
1 1
2 2
3 4
666 8
666 6
6 rows selected.
-- 回滚事务
SQL> ROLLBACK;
Rollback complete.
2.3 使用子查询进行更新
可以用子查询的结果作为指定列的值进行更新
使用子查询更新多列
示例
- 更新103号员工的工作和工资以匹配205号员工的工作和工资。
sql
-- 先查看两名员工原先的工作和工资
SQL> SELECT JOB_ID,SALARY FROM EMPLOYEES WHERE EMPLOYEE_ID=103 OR EMPLOYEE_ID=205;
JOB_ID SALARY
---------- ----------
IT_PROG 9000
AC_MGR 12008
-- 通过子查询更新
SQL> UPDATE EMPLOYEES
2 SET (JOB_ID,SALARY)=(SELECT JOB_ID,SALARY FROM EMPLOYEES WHERE EMPLOYEE_ID=205)
3 WHERE EMPLOYEE_ID=103;
1 row updated.
-- 再查看两名员工的工作和工资
SQL> SELECT JOB_ID,SALARY FROM EMPLOYEES WHERE EMPLOYEE_ID=103 OR EMPLOYEE_ID=205;
JOB_ID SALARY
---------- ----------
AC_MGR 12008
AC_MGR 12008
-- 回滚事务
SQL> ROLLBACK;
Rollback complete.
使用UPDATE语句中的子查询可基于另一个表中的值更新表中的行值:
示例
sql
UPDATE copy_emp
SET department_id = (SELECT department_id
FROM employees
WHERE employee_id = 100)
WHERE job_id = (SELECT job_id
FROM employees
WHERE employee_id = 200);
-- 查看两个子查询的结果
SQL> SELECT department_id FROM employees WHERE employee_id = 100;
DEPARTMENT_ID
-------------
90
SQL> SELECT job_id FROM employees WHERE employee_id = 200;
JOB_ID
----------
AD_ASST
-- 所以上面的示例等价于
UPDATE copy_emp
SET department_id = 90
WHERE job_id = AD_ASST;
三、DELETE | TRUNCATE 语句
删除表中存在的数据

3.1 DELETE 语句的语法
您可以使用DELETE语句从表中删除现有行:
sql
DELETE [FROM] table
[WHERE condition];
DELETE语句和UPDATE一样一定要用WHERE指定删除范围,若不指定则会直接删除整张表
3.2 通过DELETE语句删除表中的数据
- 通过DELETE语句删除表中的数据
示例
sql
-- 查看示例表
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
123 22
1 1
2 2
3 4
666 8
666 6
6 rows selected.
-- 通过WHERE子句限制删除指定的行
SQL> DELETE FROM TEST WHERE ID1=666;
2 rows deleted.
-- 查看
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
123 22
1 1
2 2
3 4
-- 回滚事务
SQL> ROLLBACK;
Rollback complete.
- 如果省略WHERE子句则删除表中的所有行
sql
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
123 22
1 1
2 2
3 4
666 8
666 6
6 rows selected.
-- 删除时不使用WHERE进行限制
SQL> DELETE FROM TEST;
6 rows deleted.
SQL> SELECT * FROM TEST;
no rows selected
SQL> ROLLBACK;
Rollback complete.
- 使用DELETE语句中的子查询根据另一个表中的值从表中删除行:
示例
sql
-- 先查看两张示例表
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
123 22
1 1
2 2
3 4
666 8
666 6
6 rows selected.
SQL> SELECT * FROM TEST1;
ID1 ID2
---------- ----------
1 1
2 2
-- 将子查询的结果作为DELETE的WHERE子句的条件
SQL> DELETE FROM TEST
2 WHERE ID1 IN (SELECT ID1 FROM TEST1);
2 rows deleted.
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
123 22
3 4
666 8
666 6
-- 提交事务
SQL> COMMIT;
Commit complete.
3.3 TRUNCATE 语句的语法和示例
从表中删除所有行,使表为空,并保持表结构完整
是数据定义语言(DDL)语句而不是DML语句; 不能轻易撤消
语法
sql
TRUNCATE TABLE table_name;
示例
sql
-- 查看示例表
SQL> SELECT * FROM TEST1;
ID1 ID2
---------- ----------
1 1
2 2
-- 使用TRUNCATE删除TEST1的数据
SQL> TRUNCATE TABLE TEST1;
Table truncated.
-- 查看TEST1
SQL> SELECT * FROM TEST1;
no rows selected
-- 尝试回滚
SQL> ROLLBACK;
Rollback complete.
-- 发现回滚也无法恢复TEST1的数据
SQL> SELECT * FROM TEST1;
no rows selected
注意:TRUNCATE删除的数据时物理删除,DELETE语句是逻辑删除,TRUNCATE语句删除后无法使用ROLLBACK语句进行回滚,而DELETE语句删除后可以回滚,并且在删除大表数据的时候TRUNCATE语句速度要比DELETE语句速度快
四、数据库事务控制:COMMIT,ROLLBACK,SAVEPOINT
4.1 数据库中的事务
在数据库中三种语言是产生事务的:
-
DML语句产生事务 INSERT UPDATE DELETE MERGE /SELECT...FOR UPDATE(特殊)
-
DDL语句产生事务 CRETAE ALTER DROP TRUNCATE
-
DCL语句产生事务 GRANT REVOKE
-
TCL COMMIT ROLLBACK 虽然他自身是控制事务的,但是在执行这两条语句的时候,在数据库底层也会发生相应的底层事务
数据库事务由以下之一组成
-
DML构成对数据的一致更改
-
一个DDL语句
-
一种数据控制语言(DCL)语句
数据库事务的开始和结束
-
从第一个DML SQL语句执行开始。
-
以下列事件之一结束:
-
发出COMMIT或ROLLBACK语句(提交或回滚)。
-
DDL或DCL语句执行(自动提交)。
-
用户正常退出SQL Developer或SQL * Plus(自动提交)。
-
系统崩溃(自动回滚)。
-
4.2 COMMIT ROLLBACK SAVEPOINT
COMMIT和ROLLBACK的优点
-
确保数据一致性
-
在永久更改之前预览数据更改
-
分组逻辑相关的操作
SAVEPOINT语句

-
使用SAVEPOINT语句在当前事务中创建一个标记。
-
使用ROLLBACK TO SAVEPOINT语句回滚到该标记。
sql
UPDATE...
SAVEPOINT update_done;
INSERT...
ROLLBACK TO update_done;
COMMIT之前或ROLLBACK之前的数据状态
-
可以恢复数据的先前状态。
-
当前会话可以通过使用SELECT语句查看DML操作的结果。
-
其他会话无法查看当前会话发出的DML语句的结果。
-
受影响的行被锁定; 其他会话无法更改受影响的行中的数据。
COMMIT之后的数据状态
-
数据更改保存在数据库中。
-
数据的先前状态将被覆盖。
-
所有会话都可以查看结果。
-
受影响行上的锁被释放; 这些行可供其他会话操纵。
-
所有保存点被删除。
ROLLBACK之后的数据状态
使用ROLLBACK语句丢弃所有未决的更改:
-
数据更改被撤消。
-
恢复数据的先前状态。
-
受影响行上的锁已释放。
回滚级别
-
如果单个DML语句在执行期间失败,则仅回退该语句。
-
Oracle服务器实现隐式保存点。
-
保留所有其他更改。
-
用户应通过执行COMMIT或ROLLBACK语句显式终止事务。
五、读取一致性
-
读取一致性可确保始终保持一致的数据视图。
-
一个用户所做的更改不会与另一用户所做的更改冲突。
-
读取一致性可确保在相同数据上:
-
Readers do not wait for writers 读不等写
-
Writers do not wait for readers 写不等读
-
Writers wait for writers 写只等写
-
5.1 实现读取一致性

六、FOR UPDATE 子句
-
数据库事务处理的关键机制,用于在多用户环境中实现行级锁定,确保数据的一致性和隔离性。它允许事务在查询数据时锁定特定行,防止其他事务同时修改这些数据,是实现悲观锁的核心手段。
-
仅当您发出ROLLBACK或COMMIT时才释放锁定.
-
如果SELECT语句尝试锁定被另一个用户锁定的行,则数据库将等待直到该行可用为止,然后返回SELECT语句的结果.
6.1 锁的简单示例
锁是oracle保证数据一致性的重要机制,当会话A对TEST表的某行进行操作时,会给该表的该行上锁,然后在会话A提交或回滚事务前,别的用户都无法再对该表的该行进行操作。
演示
sql
-- 在sql*plus中操作TEST表
-- 查看TEST表
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
123 22
3 4
666 8
666 6
-- 对TEST进行操作
SQL> UPDATE TEST SET ID1=11 WHERE ID1=666;
2 rows updated.
-- 查看TEST表,可以看见TEST表的后两行收到影响
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
123 22
3 4
11 8
11 6
-- 然后不提交事务并新开一个sql*plus会话
-- 查看TEST表,可见查询返回的结果是事务提交之前的,这是oracle的默认机制,只能查看已经提交事务的内容,未提交的是无法查看的
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
123 22
3 4
666 8
666 6
-- 新会话尝试修改后两行
SQL> UPDATE TEST SET ID1=22 WHERE ID2=8 OR ID2=6;
-- 回车后就会发现新会话卡住,无法进行
-- 然后回到旧会话提交事务
SQL> COMMIT;
Commit complete.
-- 再回到新会话查看
SQL> UPDATE TEST SET ID1=22 WHERE ID2=8 OR ID2=6;
2 rows updated.
-- 在旧会话提交事务后新会话就已经自动UPDATE了
-- 可见当某个会话修改某表的某行的时候会给该行上锁,然后别的会话就无法再修改该行了
-- 现在新会话修改了表的后两行,并且没有提交事务
-- 新会话查看TEST
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
123 22
3 4
22 8
22 6
-- 回到旧会话尝试修改前两行
SQL> UPDATE TEST SET ID2=11 WHERE ID1=123;
1 row updated.
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
123 11
3 4
11 8
11 6
-- 可见不同会话可以同时修改一个表的不同的行,这种锁是行级锁
-- 两个会话均提交事务,释放锁
6.2 FOR UPDATE使用示例
当想要锁定某行以便稍后进行修改时,可以使用FOR UPDATE 语句给该行上锁,但是前提是该行得存在,如果锁定行无数据,那么FOR UPDATE锁定将会自动撤销(失效)
sql
-- 查看TEST表
SQL> SELECT * FROM TEST;
ID1 ID2
---------- ----------
123 11
3 4
22 8
22 6
-- 对后两行上锁
-- 虽然返回的是正常的查询结果,但是这两行已经被上锁了,别的会话无法再对这两行进行操作了
SQL> SELECT * FROM TEST WHERE ID1=22 FOR UPDATE;
ID1 ID2
---------- ----------
22 8
22 6
-- 去新会话验证
SQL> UPDATE TEST SET ID2=666 WHERE ID1=22;
-- 在新会话回车后就会停住
-- 若要释放锁则使用COMMIT即可