数据操纵语言DML
当需要向数据库中添加、更新或删除数据时,需要执行数据库操纵语言DML。DML是SQL的核心部分,它包含如下几种常见的操作语句
- INSERT:向表中添加行
- UPDATE:更新存储在表中的数据
- DELETE:删除现有的行
- MERGE:插入所有的行到另一个具有现存记录的表,如果要插入的行的健匹配已存在行,则更新已存在的行而不是插入一个新行
- 在操作DML语句通常一次只能操作一个表,使用INSERT或者MERGE的变体也允许同时对多个表进行插入
- 用户必须具有在要使的DML语句的表上的权限,比如用户自己所在方案下的表
- 如果列具有NOT NULL约束,在使用DML语句时,比如INSERT或UPDATE要为具有约束的列指定值
- Oracle在用户显示地使用COMMIT语句时,才会将用户所做的更改保存回数据库,否则用户可以使用ROLLBACK语句撤销所做的任何修改
插入默认值和NULL值
默认值是在定义数据表时指定的值,如果未指定默认值DEFAULT选项,并且其NOT NULL没有被指定为True,那么将默认为NULL值,可以通过desc + 表明来查看表的架构定义
在使用INSERT语句插入数据时,需要注意避免一些常见的错误
- 对于NOT NULL列缺少强制的值:比如不允许NOT NULL的列中指定的NULL值
- 重复值违反了唯一性约束:列中已存在为一值,又输入新的唯一值
- 违反外键约束:输入的值不匹配主外键关联关系
- 违反CHECK约束:输入不匹配CHECK约束的表达式的值
- 数据类型不匹配:数据类型不匹配
- 值的宽度超过了列的限制:字符长度超出限制
如果要插入不匹配的类型,可以为列值应用转换函数来使得列值的类型匹配,或者使用系统函数来为列提供列值
使用子查询插入多行数据
通过INSERT INTO-VALUES一次只能向表中插入一行记录 ,如果要从一个已经存在的表中插入一行或多行记录,可以在INSERT语句中使用子查询从已存在的一个或多个表中复制数据
CREATE TABLE emp_copy AS SELECT * FROM emp WHERE deptno = 20;
由于emp_copy表的结构与emp完全一样,因此既可以使用SELECT * 语句进行表数据的复制,也可以在表明后制定列明
INSERT INTO emp_copy (empno, ename, job, mgr, deptno)
SELECT empno,ename,job,mgr,deptno
FROM emp
WHERE deptno = 30;
使用INSERT插入多表数据
INSERT { FIRST|ALL}
[WHEN condition THEN ] INTO table [VALUES(...)]
[WHEN condition THEN ] INTO table [VALUES(...)]
...
ELSE INTO table [VALUES(...)]
subquery;
语句关键字含义如下:
- FIRST:如果的一个WHEN子句的值为True,Oracle服务器对于给定的行执行相应的INTO子句,并且跳过后面的WHEN子句
- ALL:Oracle服务器通过相应的WHEN条件过滤每一个插入子句,确定是否执行这插入子句
- ELSE:如果条件都不满足,则执行ELSE中的插入子句
- subquery:要进行多表插入的子查询
INSERT FIRST
WHEN deptno = 10 THEN INTO emp_dept_10
WHEN deptno = 20 THEN INTO emp_dept_20
WHEN deptno = 30 THEN INTO emp_dept_30
ELSE INTO emp_copy
SELECT * FROM emp;
可以为要插入的表指定要插入的列
INSERT FIRST
WHEN deptno = 10 THEN INTO emp_dept_10(empno, ename, sal, deptno)
VALUES(empno, ename, sal, deptno)
WHEN deptno = 20 THEN INTO emp_dept_20 emp_dept_10(empno, ename, sal, deptno)
VALUES(empno, ename, sal, deptno)
WHEN deptno = 30 THEN INTO emp_dept_30 emp_dept_10(empno, ename, sal, deptno)
VALUES(empno, ename, sal, deptno)
ELSE INTO emp_copy emp_dept_10(empno, ename, sal, deptno)
VALUES(empno, ename, sal, deptno)
SELECT * FROM emp;
更新记录
使用DML语言中的UPDATE语句可以很容易的对单行数据进行更新
UPDATE table
SET column = value [, column = value, ...]
[WHERE condition];
使用SET column = value对旧列值赋新的列值,value可以是相应的值或对应列的子查询,WHERE子句中的condition确定要被更新的行,由列名,表达式,常数和比较操作符组成
UPDATE emp SET sal = 3000 WHERE empno = 7369;
UPDATE emp SET sal=3000, comm=200, mgr=7566 WHERE empno=7369;
使用子查询更新记录
UPDATE emp x
SET x.sal = ( SELECT AVG (y.sal)
FROM emp y
WHERE y.deptno = x.deptno)
WHERE x.deptno = 7369;
UPDATE emp
SET sal = (SELECT sal
FROM emp
WHERE empno = 7782)
WHERE empno = 7369
UPDATE emp x
SET (x.sal, x.comm) = (SELECT AVG(y.sal), MAX(y.comm)
FROM emp y
WHERE y.deptno = x.deptno)
WHERE x.deptno = 7369;
update emp_new x
SET (x.sal, x.comm) = (select sal, comm
from scott.emp y
where y.empno = x.empno)
where x.deptno = 7369;
update (select x.sal sal, y.sal sal_new, x.comm comm, y.comm comm_new
from scott.emp x, emp_new y
where x.empno = y.empno and x.empno = 7369)
SET sal_new = sal,
comm_new = comm;
第二种写法要求emp_new中具有主键列,也就是说不可以出现连个相同的empno编号,否则Oracle会提示错误。emp_new极有可能出现两条相同的记录,因为该表不存在主键列,但是可以使用Oracle的Hint。可以叫上述语句更改为使用了Hint的语句来实现更新成功
update /*+bypass_ujvc*/(select x.sal sal, y.sal sal_new, x.comm comm, y.comm comm_new
from scott.emp x, emp_new y
where x.empno = y.empno and x.empno = 7369)
SET sal_new = sal,
comm_new = comm;
注:Hint时Oracle提供的一种SQL语法,它允许用户在SQL语句中插入相关的语法,从而影响SQL的执行方式;oracle可以使用hints:/+ BYPASS_UJVC/ 屏蔽掉队唯一性的检查。
使用MERGE合并表行
MERGE如其名字所示,提供了在多个表之间合并数据的能力,使用该语句可以有条件的更新和插入到数据库表中,在插入数据时,如果行存在,则执行update语句进行更新,如果是一个新的行,则执行insert语句进行插入
由于MERGE命令组合了INSERT和UPDATE命令,因而需要有对目标表的INSERT和UPDATE权限,以及对源表的SELECT权限
MERGE INTO table_name table_alias
USING (table|view|sub_query) alias
ON (join condition)
WHEN MATCHED THEN
UPDATE SET
col1 = col_val1,
col2 = col2_val
WHEN NOT MATCHED THEN
INSERT (column_list)
VALUES (column_values);
语法中的关键字含义
- MERGE INTO子句:指定正在更新或插入的目标表
- USING子句:指定数据源要被更新或插入的数据来源,数据来源可以是表、视图或子查询
- ON子句:后跟条件语句,指定MERGE操作可以更新或插入
- WHEN MATCHED|WHEN NOT MATCHED:指示当匹配时,应该执行UPDATE子句进行更新,当不匹配时,应该执行INSERT语句进行插入
为了让emp_new与emp保持一致,对于存在的记录进行更新,对于不存在的记录进行删除,可以使用MERGE合并语法
MERGE INTO emp_new c --目标表
USING scott.emp e --源表
ON (c.EMPNO = e.empno)
WHEN MATCHED THEN --当匹配时,进行UPDAET操作
UPDATE
SET c.ename = e.ename,c.job = e.job, c.mgr = e.mgr,
c.hiredate = e.hiredate, c.sal = e.sal,
c.comm = e.comm,c.deptno = e.deptno
WHEN NOT MATCHED THEN --当不匹配时,进行INSERT操作
INSERT
VALUES (e.empno,e.ename,e.job,e.mgr,e.hiredate,e.sal,e.comm,e.deptno);
删除记录
使用DELETE语句删除单行记录时,注意以下要点
- DELETE语句一次只能删除整行记录,不能删除某个字段
- 与INSERT和UPDATE一样,删除一个表中的记录可能会导致与其他表的引用完整性问题
- DELETE语句只会删除记录,不会删除表,如果要删除整个表,需使用DROP TBALE命令
- 如果要一次性清除表中的数据,并且不许需要撤销删除,可以使用TRUNCATE一次性对表数据进行清除
DELETE [FROM] table [WHERE condition];
table指定要删除的表名称,WHERE子句中的condition表示被删除的行,由字段名、表达式、常数和比较操作符组成
DELETE FROM emp WHERE empno = 7903;
如果哟啊删除的行存在主外键约束,比如dept表的deptno被emp表的deptno所引用,要删除dept表中的某个的平台弄记录时,Oracle会触发异常
OAR-02292:违反完整约束条件 (SCOTT.PK_DEPTNO) - 已找到子记录
所以要先删除与其相关的子记录。
使用子查询删除记录
对于复杂的记录删除,可以使用子查询来删除基于另一个表的行
DELETE FROM scott.emp
WHERE deptno = (SELECT deptno From dept
WHERE dname = 'SALES');
在子查询中,查询dept表中dname为销售部员工记录,以返回的deptno作为DELETE语句的WHERE子句条件进行删除
可以在WHERE条件中应用IN或EXISTS来删除在其它表中粗内在的记录
使用TUNCATE清除数据
除了可以使用DELETE删除表中的内容外,还可以使用TRUNCATE语句,该语句会删除表中所有行,并且释放该表所使用的存储空间
注:TRUNCATE实际上并不是DML数据操纵语言的一部分,它属于DDL数据定义语言,与CREATE TABLE等语句一样,它不具有撤销功能,已经调用,表中的数据便会被彻底清除
TRUNCATE TABLE table_name
与DELETE相比,使用TRUNCATE命令速度要快一些,这是由如下3个原因决定的
- TRUNCATE语句不会激活表删除触发器
- TRUNCATE语句属于数据定义语言DDL语句,不会产生撤销信息
- 如果表是主外键关系的主表,则无法清除表的内容,必须在执行TRUNCATE语句之前禁用该约束
注:TRUNCATE属于DDL语句,因此不能被PL/SQL语句块直接调用,必须要使用动态语句调用方式
为了能正确的清除表内容,必须首先禁用约束,然后使用TRUNCATE TABLE进行表内容的清除
ALTER TABLE dept DISABLE CONSTRAINT pk_dept CASCADE;