数据更新、事务处理、数据伪列
一、数据的更新操作
DML操作语法之中,除了查询之外还有数据的库的更新操作,数据的更新操作主要指的是:增加、修改、删除数据,但是考虑到emp表以后还要继续使用,所以下面先将emp表复制一份,输入如下指令:
CREATE TABLE myemp AS SELECT * FROM emp;
这种语法是Oracle中支持的操作,其他数据库不一样。
1、数据增加
如果现在要想实现数据的增加操作,则可以使用如下的语法完成:
INSERT INTO 表名称 [(字段1,字段2,…)] VALUES(值1,值2,…);
如果要想进行增加数据的话,则以下的几种数据类型要分别处理:
-
增加数字:直接编写数字,例如:123;
-
增加字符串:字符串应该使用"'"声明;
-
增加DATE数据:
-
第一种:可以按照已有的字符串的格式编写字符串,例如"'17-12月-80'";
-
第二种:利用TO_DATE()函数将字符串变为DATE型数据;
-
第三种:如果设置的时间为当前系统时间,则使用SYSDATE;
-
可是对于数据的增加也有两种操作格式:完整型、简便型;
范例:增加数据 —— 完整语法编写
INSERT INTO myemp(empno,ename,hiredate,sal,mgr,job,comm)
VALUES(8888,'张三',TO_DATE('1960-08-17','yyyy-mm-dd'),8000,7369,'清洁工',1000);
INSERT INTO myemp(empno,ename,hiredate,sal,mgr,job,deptno)
VALUES(8889,'李四',SYSDATE,3000,7369,'清洁工',30);
范例:增加数据 —— 简便语法,不写增加的列名称
INSERT INTO myemp VALUES(8889,'李四','清洁工',7369,SYSDATE,3000,null,30);
很明显虽然简便语法代码少了,可是这种操作并不可取,所以在实际的开发之中,没有任何一个人会写简便语法,这样根本就不利于程序的维护,都编写完整的操作。
2、数据修改
如果要想修改表中已有的数据,则可以按照如下的语法进行:
UPDATE 表名称 SET 更新字段1=更新值1,更新字段2=更新值2,… [WHERE 更新条件(s)];
范例:更新雇员编号是7369的基本工资为5000,奖金为2000,职位修改为MANAGER,雇佣日期修改为今天
UPDATE myemp SET sal=5000,comm=2000,job='MANAGER',hiredate=SYSDATE WHERE empno=7369;
范例:所有人的工资改为7500
UPDATE myemp SET sal=7500;
如果更新时候不加上更新操作,则意味着更新全部的数据,但是这种做法绝对不可取,如果现在表中的数据量较大的话,这种更新所耗费的时间是相当长的,而且性能也会明显降低。
3、数据删除
当数据不再需要之后,就可以使用以下的语法删除数据:
DELETE FROM 表名称 [WHERE 删除条件(s)];
与更新一样,如果没有写删除条件的话,则表示删除全部数据;
范例:删除所有在1987年雇佣的雇员
DELETE FROM myemp WHERE TO_CHAR(hiredate,'yyyy')=1987;
一定要记住,如果删除的时候没有匹配条件的数据存在,则更新的记录为"0",包括更新操作也是一样的。
范例:删除表中的全部记录
DELETE FROM myemp;
一般对于删除的操作而言,尽可能少去使用,包括以后在进行系统开发的时候,对于所有的删除操作之前实际上都建议大家先给出一个确认的提示框,以防止用户误删除。
二、事务处理
对于数据表的操作,很明显查询要比更新操作更加的安全,因为更新操作有可能会出现错误,以导致没有按照既定的要求正确的完成更新操作。
但是在很多时候更新有可能会有多条指令共同完成,例如:以银行转帐的方式为例:
-
判断A的账户上是否有5000W;
-
判断B账户是否存在并且状态是否正常;
-
从A的账户上移走5000W;
-
向B的账户之中增加5000W;
-
向银行支付手续费用5000;
以上的五个操作是一个整体,可以理解为一个完整的业务,如果这之中第三点出错了,那么其他的操作呢?所有的操作都应该不再执行,并且回归到最原始的状态,而这个操作流程就是事务的操作。
所有的事务处理操作都是针对于每一个session进行的,在Oracle数据库之中,把每一个连接到数据库上的用户都称为一个session,每一个session之间彼此独立,不会有任何的通讯,而每一个session独享自己的事务控制,而事务控制之中主要使用两个命令:
-
事务的回滚:ROLLBACK,更新操作回到原点;
-
事务的提交:COMMIT,真正的发出更新的操作,一旦提交之后无法回滚;
但是这样一来也会出现一些问题,例如:某一个session在更新数据表的时候还没有提交事务,其他session是无法更新的,必须等待之前的session提交后才可以;
这种问题从大的方面讲可以称为死锁,但是在Oracle之中死锁有很多的种类,这些与开发者没有太大的关系,是由DBA负责的。
所有的数据更新一定都会受到事务的控制。
三、数据伪列
数据伪列指的是用户不需要处理的列,而是由Oracle自行维护的数据列,在Oracle之中有两个数据伪列:ROWNUM、ROWID;
1、 ROWNUM
ROWNUM从单词含义上讲应该表示的是行号,实际上ROWNUM为每一个显示的记录都会自动的随着查询生成行号,例如,下面通过两个代码观察:
SELECT ROWNUM,empno,ename,job,hiredate,sal FROM emp;
此时的ROWNUM行号并不是永久固定的;
SELECT ROWNUM,empno,ename,job,hiredate,sal FROM emp WHERE deptno=30;
是每次动态的重新生成的,那么既然有了ROWNUM之后,下面就可以实现数据的部分显示;
范例:查询前5条记录
SELECT ROWNUM,empno,ename,job,hiredate,sal FROM emp
WHERE ROWNUM<=5;
范例:查询6-10条记录
按照正常的思维肯定是直接进行BETWEEN…AND的判断;
SELECT ROWNUM,empno,ename,job,hiredate,sal FROM emp
WHERE ROWNUM BETWEEN 6 AND 10;
这个时候并没有返回任何的数据,因为ROWNUM不是真实列,而要想真正的实现这种查询,思路是:先查询前10条记录,之后再显示后5条记录,要依靠子查询完成。
SELECT * FROM(
SELECT ROWNUM m,empno,ename,job,hiredate,sal FROM emp
WHERE ROWNUM<=10) temp
WHERE temp.m>5;
如果现在按照这个思路,下面就可以给出日后程序中所需要分页的功能实现。
范例:显示前5条记录
当前所在页(currentPage)为1;
每页显示的记录长度(lineSize)为5;
第一页:
SELECT * FROM(
SELECT ROWNUM m,empno,ename,job,hiredate,sal FROM emp
WHERE ROWNUM<=5) temp
WHERE temp.m>0;
范例:显示中间的5条记录
当前所在页(currentPage)为2;
每页显示的记录长度(lineSize)为5;
第二页:
SELECT * FROM(
SELECT ROWNUM m,empno,ename,job,hiredate,sal FROM emp
WHERE ROWNUM<=10) temp
WHERE temp.m>5;
范例:显示第三页的内容,currentPage=3,lineSize=10;
SELECT * FROM(
SELECT ROWNUM m,empno,ename,job,hiredate,sal FROM emp
WHERE ROWNUM<=15) temp
WHERE temp.m>10;
以上的程序就是分页显示操作的核心代码。
2、 ROWID
ROWID表示的是每一行数据保存的物理地址的编号,例如,观察如下的查询:
SELECT ROWID,deptno,dname,loc FROM dept;
此时返回许多的记录:
ROWID DEPTNO DNAME LOC
------------------ ---------- -------------- -------------
AAAL+XAAEAAAAANAAA 10 ACCOUNTING NEW YORK
AAAL+XAAEAAAAANAAB 20 RESEARCH DALLAS
AAAL+XAAEAAAAANAAC 30 SALES CHICAGO
AAAL+XAAEAAAAANAAD 40 OPERATIONS BOSTON
每一条记录的ROWID都不会重复,所以即便表中所有列的数据内容都重复了,ROWID也是不会重复的,而且以一个ROWID为例,说明ROWID组成,例如:AAAL+XAAEAAAAANAAA
-
数据对象号:AAAL+X;
-
相对文件号:AAE;
-
数据块号:AAAAAN;
-
数据行号:AAA;
面试题:请删除表中的重复记录
现在项目之中由于管理不善,所以导致出现了许多重复的信息,为了说明问题,下面为表中增加若干条记录。
INSERT INTO dept(deptno,dname,loc) VALUES (21,'RESEARCH','DALLAS');
INSERT INTO dept(deptno,dname,loc) VALUES (31,'SALES','CHICAGO');
INSERT INTO dept(deptno,dname,loc) VALUES (32,'SALES','CHICAGO');
INSERT INTO dept(deptno,dname,loc) VALUES (41,'OPERATIONS','BOSTON');
INSERT INTO dept(deptno,dname,loc) VALUES (42,'OPERATIONS','BOSTON');
INSERT INTO dept(deptno,dname,loc) VALUES (43,'OPERATIONS','BOSTON');
COMMIT;
此时的数据显示是:
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
21 RESEARCH DALLAS
31 SALES CHICAGO
32 SALES CHICAGO
41 OPERATIONS BOSTON
42 OPERATIONS BOSTON
43 OPERATIONS BOSTON
已选择10行。
现在要求删除掉所有重复的记录,保留最早增加的记录内容。
最终显示效果:
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
先按照查询的方式做,首先找到所有重复的数据,重复的数据就比较容易了,按照部门名称及位置分组,之后统计个数,如果个数大于1,则表示重复。
SELECT dname,loc,MIN(ROWID)
FROM dept
GROUP BY dname,loc
HAVING COUNT(deptno)>1;
此时返回的三个ROWID,与最早的ROWID比较一下。
ROWID DEPTNO DNAME LOC
------------------ ---------- -------------- -------------
AAAL+XAAEAAAAANAAA 10 ACCOUNTING NEW YORK
AAAL+XAAEAAAAANAAB 20 RESEARCH DALLAS
AAAL+XAAEAAAAANAAC 30 SALES CHICAGO
AAAL+XAAEAAAAANAAD 40 OPERATIONS BOSTON
SQL> SELECT dname,loc,MIN(ROWID)
2 FROM dept
3 GROUP BY dname,loc
4 HAVING COUNT(deptno)>1;
DNAME LOC MIN(ROWID)
-------------- ------------- ------------------
SALES CHICAGO AAAL+XAAEAAAAANAAC
RESEARCH DALLAS AAAL+XAAEAAAAANAAB
OPERATIONS BOSTON AAAL+XAAEAAAAANAAD
数据插入的早,ROWID就小,所以下一步首先必须确定的是不能删除的ROWID,去掉之前的限制(COUNT()>1),表示按照部门名称及位置分组,取出每一个最小的ROWID;
SELECT MIN(ROWID)
FROM dept
GROUP BY dname,loc;
以上的数据是删除的,所以下面编写DELETE语句:
DELETE FROM dept
WHERE ROWID NOT IN(
SELECT MIN(ROWID)
FROM dept
GROUP BY dname,loc);
COMMIT;
此时就是根据ROWID完成的,而这道题目也就是ROWID现在为止的唯一可以看的见的作用。
在所有的伪列之中,只有ROWNUM是以后开发之中最为重要的部分,一定要掌握,而对于ROWID知道就行了,不必做太深入的了解。