
关键字before和after用于标识触发时间,顾名思义,before代表触发器里面的命令在DML修改数据之前执行,after代表触发器里面的命令在DML修改数据之后执行。 读者可以结合应用场景选择使用before或者after。 下面编者分别给出before和after的举例。 before举例: 在emp表上面创建触发器,当输入的工资小于100时,自动将工资修改为100。 create or replace trigger tri_emp_sal_check before INSERT OR UPDATE ON emp for each row BEGIN IF :new.sal <100 THEN :new.sal :=100; END IF; END; 执行DML语句 update emp set sal =90 where empno =3030; commit; 发现工号3030员工的工资成功修改为100。 上面的触发器,我们尝试将before修改为after。编译的时候报错,不能通过。错误信息如下: 编者前面已经说过,after代表触发器里面的命令在DML修改数据之后执行,既然DML操作对数据的更改已经完成,当然不允许再修改new对象的属性值了,所以此处编译不通过。 after举例: 在emp表上面创建触发器,当修改员工工资时,记录日志。为了更明显的显示效果,编者在此触发器中使用了自治事务。并且在emp表的sal列上增加了check约束,限制sal的值大于等于100。 create or replace trigger tri_emp_sal_change after UPDATE ON emp for each row DECLARE V_ID NUMBER; pragma autonomous_transaction; BEGIN IF updating('SAL')THEN SELECT LOG_ID.NEXTVAL INTO V_ID FROM DUAL; insert into log (ID,CONTENT, IP, COMPUTERNAME) values (v_ID, :new.empno ||'工资' ||'由' || :old.sal ||'变更为' || :new.sal, sys_context('userenv','ip_address'), sys_context('userenv','terminal')); commit; END IF; END; 接着编者执行如下update命令 update emp set sal =90 where empno =3030; 报违反检查约束条件错误,如下图所示。 编者查看log表,发现log表为空。如下图所示。 说明使用after的情况下,触发器里面的命令在DML修改数据之后执行,此时log中无记录,是因为DML修改数据时违反检查约束条件,触发器里面的命令没有机会执行。 接着编者将触发器中的after替换成before。 执行相同的update命令 update emp set sal =90 where empno =3030; 同样违反检查约束条件,报错如下图所示。 编者接着查询log表,发现成功记录了修改日志。 说明使用before的情况下,触发器里面的命令在DML修改数据之前执行,此处虽然DML修改数据时违反检查约束条件,没有执行成功,但是由于触发器里面的命令先于DML修改数据之前执行,并且此处使用了自治事务,使得触发器里面的命令得以成功执行。 到底应该使用before还是after,要根据具体情况而定,一般需要更改要操作的数据时使用before。如果记录日志要使用after。只有使用了after才能确保DML语句成功执行,这样记录日志才有意义。
分组函数最多嵌套两层,当三层或者三层以上时,就会出现分组函数的嵌套太深的提示。 我通过下面一条查询一句进行分析。 select count(avg(sum(sal))) from emp group by deptno; 这条查询语句,是根据部门号进行分组,第一层分组函数是求每个部门的工资支出和,当第一层分组函数再嵌套一层分组函数求所有部门工资支出的平均值时,结果就只剩一行了,一行结果就没必要再分组了,所以当嵌套第三次分组函数时,编译器就会报分组函数的嵌套太深的错误。
Null的含义是不确定的意思,在oracle数据库中 ‘’代表的也是null。由于null的特殊性,null有着自己的判断方法。下面两条查询语句相信很多读者都碰到过,甚至无意写过。当null跟在等于后面时,是查询不到记录的。 select * from emp where job = null or job =’’; select * from emp where job <> null and job <> ‘’; 正确的写法应该是: select * from emp where job is null; select * from emp where job is not null;
大家是不是也经常碰到单行子查询返回多个行的错误提示。我已经碰到数次这种错误了,这种错误是基于数据存在的,开发人员也许在最初写这个查询语句的时候没有合适的数据,所以没有发现错误,当满足条件的数据出现后才会给出这种错误提示,特别是非常复杂的查询语句,排查原因变得非常困难。 我列举两条简单的查询语句供读者学习。 1.select * from emp where job =(select job from emp a where a.job = emp.job) 2.select (select ename from emp a where a.job = emp.job) from emp 第一条查询语句,where条件里面的等于明显是单行比较符,当等于后面的子查询返回多行时,就会出现单行子查询返回多个行的错误提示。 第二条查询语句,子查询的结果是作为父查询的一个列,所以也要求返回单行,当子查询出现多条返回记录时,同样会出现单行子查询返回多个行的错误提示。
关于count方法一直存在争议,count(*)、count(列名)、count(常量),到底应该使用哪个?我认为应该是count(*),count(*)是SQL92定义的标准统计数的方法。我曾经有幸阅读过阿里巴巴的开发规范,里面提到强制使用count(*)。 我通过以下四个查询命令来说明三个方法的区别: 1.select count(*) from emp; 2.select count(1) from emp; 3.select count(job) from emp; 4.select count(*) from emp where jobis not null; 第一条命令查出的结果是员工的数量。 第二条命令查出的结果是员工的数量。 第三条命令查出的结果是工种不为空的员工的数量。 第四条命令查出的结果是工种不为空的员工的数量。 显然,第一条与第二条的效果相同,第三条与第四条的效果相同。 至于1,2为什么选择1,因为1大家都能看懂,2已经不止一个人问过我是什么意思。至于3,4为什么选择4,因为4大家都能看懂,3会给不同的读者带来不同的理解。 有部分人可能会说count(*)没有count(1)的效率高,我在一张包含33554432条记录的表中,使用count(*)和count(1)测试,所消耗的时间都是3到4秒之间。 所以,大家完全可以不要因为效率问题而选择使用count(1)了。