什么是数据库?
数据库是用户存放数据、访问数据、操作数据的存储仓库;
数据库具有较高的独立性,即不依赖特定的数据库应用程序
数据库系统的数据冗余小,可以节省数据的存储空间;
数据库系统还很容易实现多个用户的数据共享
关系型数据库
关系型数据库指的基于关系模型的数据库,关系模型就是由表组成,通过行和列来存储数据,每个表都有一定的联系
关系型数据库有
Oracle
DB2 千万级
Sybase
MS SQL Server
MySQL(开源) 百万级
PostgreSQL(开源)
Oracle数据库包括逻辑结构和物理结构
物理结构包含数据库中的一组操作系统文件
逻辑结构值数据库创建之后形成的逻辑概念之间的关系
一、Oracle默认用户
Oracle有几个默认的数据库用户:
Sys是系统最高权限的管理员
SYSTEM 是默认的系统管理员,该用户拥有Oracle管理工具使用的内部表和视图。通常通过SYSTEM用户管理数据库用户、权限和存储等
SCOTT用户是Oracle 数据库的一个示范帐户,在数据库安装时创建。(测试帐号)。
二、创建新用户
每个用户都有一个默认的表空间和一个临时表空间
语法
create user 用户名 identified by 密码;
锁定用户
ALTER USER myuser ACCOUNT LOCK;//锁定用户,锁定后不能登录,必须由系统最高权限的用户才能对其他用户进行锁定
ALTER USER myuser ACCOUNT UNLOCK;//解锁用户
更改和删除用户
alter user 用户名 identified by 新密码
drop user 用户名 cascade;使用cascade子句删除所有关联的对象
三、授予权限
权限有两种类型,系统权限和对象权限
系统权限运行用户执行某些数据库操作,如创建表就是一个系统权限
对象权限允许用户对数据库对象(表、视图、序列等)执行特定的操作
角色是一组相关权限的组合,可以将权限授予角色,再把角色授予用户,以简化权限管理
授予系统权限
grant connect to 用户名:允许用户连接至数据库,并创建数据库对象
grant resource to 用户名:允许用户使用数据库中的存储空间
grant create sequence to 用户名;允许用户在当前模式中创建序列
grant select on test to 用户名;允许用户查询test表的记录
grant update on test to 用户名;允许用户更新test表中的记录
grant all on test to 用户名;允许用户插入、删除、更新和查询test表中的记录
grant all privileges to 用户名;把所有权限授予此用户
收回系统权限
revoke create table from 用户名; 表示回收创建序列权限
授予对象权限
grant select on scott.emp to 用户名 with grant option;
收回对象权限
revoke select on scott.emp from 用户名;
四、表空间分类
永久表空间:存储数据库中需要永久化存储的对象,比如二维表、视图、存储过程、索引
临时表空间:存储数据库的中间执行过程,一般只有tmp一个临时表空间,如果还需要别的临时表空间,可以自己创建
undo表空间:保存数据修改前的副本
什么是表空间
表空间是数据库的逻辑划分,一个表空间只能属于一个数据库,所有的数据库对象都存放在指定的表空间,但主要存放的是表,所以称作表空间
Oracle数据库中至少存在一个表空间,即system的表空间
表空间的作用
控制用户所占用的表空间配额。
控制数据库所占用的磁盘空间。
灵活放置表空间,提高数据库的输入输出性能。
大表的排序操作。
日志文件与数据文件分开放,提高数据库的安全性
创建表空间
create tablespace 表空间名
DATAFILE 'C:\oracle11g\oradata\orcl\tb_myspace.dbf' 路径
SIZE 20M; 空间大小
AUTOEXTEND OFF ;
删除表空间
drop tablespace tbs_myspace INCLUDING CONTENTS AND DATAFILES;
加上AND DATAFILES子句选项的话物理文件可以一起删除
五、数据类型
六、char、varchar 和varchar2的区别
char的长度是固定的,而varchar2的长度是可以变化的,比如,存储字符串“ABC”,对于char(20),表示你存储的字符将占用20个字节(包括17个空字符),而同样的varchar2(20)则只占用3个字节的长度,20只是最大值,当你存储的字符小于20时,按实际长度存储。
char的效率比varchar2的效率稍高。
varchar是varchar2的同义词
varchar可以存储空字符串
varchar2可以存储null值
七、sql语句主要可以划分为以下几类:
DDL(Data Definition Language):数据定义语言,定义对数据库对象的操作(create drop alter rename truncate)
DML(Date Manipulation Language):数据操作语言,定义对数据库记录的操作(insert、delete、update)
DQL(Data Query Language):数据查询语言,定义对数据库记录的查询操作(select)
DCL(Data Control Language):数据控制语言,定义对数据库、表、字段、用户的访问权限和安全级别(grant revoke)
Transaction Control:事务控制
commit,rollback,savepoint
sql语句书写规范
Sql语句不区分大小写(建议用大写)
字符串常量区分大小写
Sql语句克单行或多行书写,以“;”结尾
关键词不能跨行或简写。
注释:/**/多行注释,-- 单行注释
表命名规则
必须以字母开头
字符长度在1-30之间
只能包含A-Z,a-z,0-9,_,$,#
被一个用户拥有的对象不能有重复的名字
不能是oracle服务器保留字
八、create语法使用
创建表
create table 表名(字段名 字段类型);如果想在别的用户下建表,就用指定用户名.表名,如不然,就默认在当前用户下建表
使用子查询语法创建表
CREATE TABLE 表名AS SELECT * FROM 要拷贝的表名(已存在的);
九、alter语法使用
添加一个字段
ALTER TABLE 表名ADD 字段名 数据类型;增加多个用括号括起来
修改字段
可以改变字段的数据类型,大小和默认值,当字段只包含空值时,才可以修改,否则修改可能不成功
ALTER TABLE 表名MODIFY 字段名 新的数据类型;
修改字段名
ALTER TABLE 表名RENAME COLUMN 旧的字段名TO 新的字段名;
删除字段
ALTER TABLE 表名DROP COLUMN 字段名;
十、表操作
修改表名
RENAME tmp1 TO 表名;注意:只能修改当前用户下的表名
删除表
DROP TABLE 表名
恢复删除的表
FlashBack table 表名 to before drop;
截断表(清空表)
TRUNCATE TABLE 表名;
查看所有表
SELECT * FROM tab;
约束和dml语句
数据完整性
数据完整性是指数据的精确性和可靠性
数据完整性指存储在数据库中的所有数据值均正确状态
什么是约束
约束是在表上强制执行的数据校验规则
约束主要用于保证数据库的完整性
当表中数据有相互依赖性时,可以保护相关的数据不被删除
约束有:
NOT NULL 非空
PRIMARY KEY 主键
UNIQUE 唯一
FOREIGN KEY 外键
CHECK 检查
DEFAULT 默认值
按照约束的创建方式分为列级约束与表级约束
列级约束直接在创建表时跟在列后定义 ,不需要指定列名,与列定义直接用空格分开
表级约束是创建表之后追加约束,要指定对哪些列建立约束
列级约束
非空 NOT NULL
主键 PRIMARY KEY
唯一 UNIQUE
外键 CONSTRAINT 表名FOREIGN KEY (字段名) REFERENCES 父级表名(父级字段名)
检查 CHECK (表达式)
默认值 DEFAULT(‘默认内容’)
复合主键 primary key(字段名,字段名)
表级约束
主键约束
ALTER TABLE tb_test ADD CONSTRAINT pk_testid PRIMARY KEY(testid)
唯一约束
ALTER TABLE tb_test ADD CONSTRAINT uq_testName UNIQUE(testName)
外键
ALTER TABLE 表名ADD CONSTRAINT 表名 FOREIGN KEY(字段名) REFERENCES tb_dept(字段名);
非空
alter table 表名 modify 字段名not null
删除约束
ALTER TABLE table DROP PRIMARY KEY CASCADE;删除主键约束,并删除相关外键约束
alter table 表名 modify 列名null 删除非空约束
查询语句
运算符的优先级
乘法和除法的优先级高于加法和减法
同级运算的顺序是从左到右
表达式中使用括号可以强行改变优先级的运算顺序
select a*(b+c) from emp;
字符串的连接操作
将列或字符与其它列连接
select name||’xxxx’||age from stu;
Null值的使用
空值是指不可用、为分配的值
空值不等于零活空格
任意类型都可以支持空值
包括空值的任何算数表达式都等于空
字符串和null进行连接运算,得到的也是null
定义字段的别名
用于表示计算结果的含义
select name as”姓名” from stu;
重复记录
使用distinct清除重复行
distinct的作用范围是后面所有字段的组合
select distinct name from stu;
where中的比较运算符
运算符 | 含义 |
= | 等于 |
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
<>、!= | 不等于 |
运算符 | 含义 | 使用方法 |
between …and… | 在两个值之间(包含) | select * from stu where age between 10 and 20 |
in(list) | 匹配所有列出的值 | select * from stu where stu_id in(1,2,3); |
like | 匹配一个字符串模式 | select * from stu where name like ‘%周%’; |
is null | 为null值 | select * from stu where id is null; |
逻辑运算符
运算符 | 含义 | 使用方法 |
AND | 如果组合条件都是TRUE,结果为TRUE | select * from stu where name=’周’and age=12; |
OR | 如果组合条件一边是TRUE,结果为TRUE | select * from stu where name=’周’or name=’雷’; |
NOT | 如果条件为FALSE,结果为TRUE。就是取反的意思 | select * from stu where job_id not in(12,2,1); |
对结果集排序
desc;降序
asc;升序
Select * from stu order by id desc;
聚合函数
运算符 | 含义 | 使用示例 |
COUNT(*|列名) | 统计行数 | select count(*) from stu |
AVG(数值型列名) | 平均值 | select avg(id) from stu |
SUM(数值型列名) | 求和 | select sum(id) from stu |
MAX(列名) | 最大值 | select max(id) from stu |
MIN(列名) | 最小值 | select min(id) from stu |
GROUP BY 子句
GROUP BY 子句作用于在与各种聚合函数配合使用,它用来对查询出来的数据进行分组
分组的含义是:把该列具有相同值的多条记录当成一组记录处理,最后只输出一条记录。
SELECT deptno,AVG(sal) FROM TB_EMP GROUP BY deptno
限定组的结果:HAVING子句
Where和having都是用来做条件限定的,但是having只能用在group by之后
SELECT deptno,AVG(sal) FROM TB_EMP GROUP BY deptno HAVING AVG(sal)>200;
Oracle中的分页查询
SELECT * FROM scott.emp WHERE ROWNUM<=4;必须是小于等于4,如果直接等于4的话,就会没有结果,因为它不知道从哪里开始
SELECT * FROM(SELECT ROWNUM r, empno FROM scott.emp WHERE ROWNUM<=4)e WHERE e.r>=2; 结果显示2~4条,包括2不包括4
标准查询语句
SELECT * FROM (SELECT s.sno ,ROWNUM r FROM (SELECT sno FROM student)s WHERE ROWNUM<=5)c WHERE c.r>=3;
HAVING与WHERE的区别
where是分组前进行条件过滤,having是在分组后进行条件过滤
where子句中不能使用聚合函数,having子句可以使用聚合函数
多表连接查询
等值连接
查询员工信息:员工号 姓名 月薪 部门名称
select e.empno,e.ename,e.sal,d.name from emp e,dept d where e.deptno=d.deptno;
e表示emp表的别名,d表示dept表的别名
非等值连接
外连接
外连接分为三种:左外连接,右外连接,全外连接,对应SQL:LEFT/RIGHT/FULLOUTER JOIN。通常我们省略outer 这个关键字。写成:LEFT/RIGHT/FULL JOIN。
对于外l连接,Oracle中也可以使用”(+)”来表示,注意事项:
- (+)操作符只能出现在where子句中,并且不能与outer join语法同时使用
- 当使用(+)操作符执行外连接是,如果在where子句中包含有多个条件,则必须在所有条件中都包含(+)操作符
- (+)操作符值试用于列,而不能用在表达式上。
- (+)操作符不能与or和in操作符以前使用。
- (+)操作符只能用于实现左外连接和右外连接,而不能用于实现全外连接
按部门统计员工人数:部门号 部门名称 人数
select d.deptno,d.dname ,count(e.emno)
from emp e,dept d where e.deptno(+)=d.deptno group by d.deptno,d.dname;
外连接分为左外连接和右外连接
左外连接
当where e.deptno=d.deptno不成立的时候,等号左边的表依然被包含
写法:where e.deptno=d.deptno(+)
右外连接
当where e.deptno=d.deptno不成立的时候,等号右边的表依然被包含
写法:where e.deptno(+)=d.deptno
自连接
通过表的别名,将同一张表视为多张表
Select e.ename ,b.ename from emp e,emp b where e.mgr=b.empno;
内连接与外连接的区别
两个表(或连接)中某一数据项相等的连接称为内连接,内连接的方式是在“设置表间关联关系”的界面中选择“=”,所以内连接又称为等值连接,连接的结果是形成一个新的数据表
内连接取数据交集,
外连接分左和右,左连接左边的数据全取,右连接右边的数据全取
子查询
子查询分为单行和多行
单行子查询值返回一条结果,可以使用单行比较运算符 =、>、>= 、< 、<=、 <>
多行子查询返回多条结果,可以使用多行比较运算符In 、any、 all
可以在where select having from 后面都可以使用子查询
select
select empno,ename,sal,(select job from emp where empno=12) from emp;将子查询当做一列
查询结果将会出来四列
from
select * from(select empno,ename,sal from emp);将子查询当做表
查询结果出现三列
查询部门名称是sales的员工
方式一
select * from emp where deptno=(select deptno from dept where dname=’sales’);
方式二
select e.* from emp e,dept d where e.deptno=d.deptno and d.dname=’sales’;
sql优化:尽量使用方式二,因为多表查询效率更高,比如方式一有两个from就要访问数据库两次,方式二只要访问一次,所有说多表查询比子查询效率高
查询工资比30号部门所有员工高的员工信息
select * from emp where sal>all(select sal from emp where deptno=30);
常用函数
函数分为单行函数和多行函数
单行函数
作用于每行
每行返回一个值
可以改变数据类型
可以嵌套使用
可以接受列或者表达式作为参数
单行函数又分为字符函数、数值函数、通用函数、转换函数、日期函数。
字符串函数
大小写转换函数
LOWER:字符串小写
UPPER:字符串大写
INITCAP:每单词首字母大写,任何符号可以做分隔符
initcap('smith abc aBC')首字母变大写,其他字母变小写
字符串处理函数
CONCAT:连接两个字符串concat('010-','88888888')
SUBSTR:取出子串 SUBSTR('abc',2)表示从第二个开始取
LENGTH:返回字符串长度
INSTR:返回子串位置instr('重庆某软件公司','某',3,2)第一个参数是被搜索的字符串,第二个参数是要搜索的内容,第三个参数是从第几位开始搜索,第四个参数是几次出现的位置
LPAD|RPAD:以空格补齐宽度
LPAD(1,2,3);如果参数1大于参数2,那么就返回长度为参数2的字符串。如果参数1小于参数二,那么就在参数1左边(READ则是在右边)补齐参数2减参数1个参数3并返回
TRIM/LTRIM/RTRIM:去掉空格
REPLACE:替代字符串
replace('he love you','he','I')表示把he替换成i,如果参数2不存在,就不替换
LTRIM('gao qian jing','gao q')删除指定字符
数学函数
ceil();ceil(22.7);-- 向上取整
floor():floor(22.7);-- 向下取整
round(a,b);可保留小数位数,如b为负数,
trunc(a,b);与round(a,b)相似,只是不四舍五入
sqrt(a);获得a的平方根,a必须为正数
mod(a,b);求余,a取余b
sign(n);n>0,返回1;n<0,返回-1;否则返回0
power(a,b);power(2,5);-- 返回x的y次幂
日期函数
months_between(a,b);返回a,b之间月的数量,a,b可为字符串
add_months(a,b);返回日期d加上b个月后的日期,b是整数
next_day(a,星期几);返回日期d后第一个星期几的日期
last_day(a);返回a所在月最后一天的日期
round(d[,fmt]);将日期d按格式fmt来四舍五入
trunc(d[,fmt]);与round相似,只是不四舍五入
SELECT SYSDATE FROM dual;返回当前系统时间
嵌套函数
单行函数可以嵌套
嵌套函数的执行顺序是由内到外
F3(F2(F1(col,arg1),arg2),arg3)
转换函数
TO_CHAR(date, 'fmt')将日期或数据转换为char数据类型
to_char(1210.73, '$9,999.00');
to_char(sysdate,'yyyy');sysdate是取当前系统时间
TO_DATE(date,'fmt')将字符串转化为日期型
to_date('199912','yyyymm')
TO_NUMBER(char [, 'fmt']))将字符串X转化为数字型
TO_NUMBER('199912')
其他函数
SELECT nvl('','')FROM dual;-- 如果前面参数为null返回后面参数,后面参数为null返回前面参数
SELECT nvl2('2','1','2')FROM dual;-- NVL2(expr,expr1,expr2) 如果expr不为null,返回expr1, 为null,返回expr2。
SELECT DECODE('3','1','ok','2','no','sss') FROM dual;
--DECODE(‘列’,值1,结果1,值2,结果2…..值n,结果n,else
表示值1如果等于列的话就范湖结果1,值2等于列的话就返回结果2,都不等于就返回最后一个结果else
数据库对象
序列
序列的概念
序列是一个数据库的内置对象
产生一系列唯一的数值
序列的作用
实现类似sql server中主键自增的效果
创建序列
CREATE SEQUENCE myseq;创建一个名称为myseq的序列
increment by 5;每次递增值,默认为1
start with 5;从第几位数开始,默认为1
maxvalue 1;最大值
minvalue 2;最小值
使用序列
INSERT INTO testseq VALUES(myseq.nextval,'abc');
修改序列
序列一旦创建就不能修改起始参数
alter sequence 序列名
increment by 5;每次递增值,默认为1
maxvalue 1;最大值
minvalue 2;最小值
删除序列
drop sequence 序列名;
nextval和currval伪列
nextval返回下一个可用的序列值,当它被使用时,每次都会返回一个唯一值,即使对于不同用户
currval获取当前的序列值
注意:至少要执行nextval一次,currval才会有值,因为一开始指针是指向数组前的空值
同义词
通过创建同义词(对象的另一个名字)简化访问对象的操作
作用
使参考到被另外一个用户拥有的表更加容易
缩短对象名称长度
保护数据安全
为scott用户下的emp表创建一个同义词
create synonym 同义词名 from scott.emp;
删除同义词
drop synonym 同义词名
索引
索引是与表相关的一种数据库逻辑存储结构,也可以被视为是数据库中用于存放表中每一条记录位置的一种对象
索引的优点
通过创建唯一性索引,可以保证数据库中每一行数据的唯一性
提高查询速度
可以加速表和表之间的连接
在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间
提高系统的性能
索引的缺点
创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加
索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大
当对表中的数据进行增加,删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度
索引的分类
B-Tree索引 Oracle中的默认索引,一种树形结构的索引,B-Tree索引可以是一个列的(简单)索引,也可以是复合索引。B-Tree最多可以包括32列
位图索引 主要针对大量相同的值的列而创建
Hash索引 使用hash索引必须要使用hash集群,建立一个集群或hash集群的同时,也就定义了一个集群键
聚族索引 聚族索引的组织顺序和数据本身的组织顺序是一致的,这也解释了数据库中只能定义一个聚族索引的原因,因为数据本身只能按一种方式进行排序
非聚族索引 其索引组织顺序和数据组织顺序不一致,因此非聚族索引可以创建多个
函数索引
分区索引
创建索引
create unique index 索引名 on 表名(字段名)
索引的修改和删除
alter index 索引名 rebuild storage(initlal 1m next 512k)
drop index 索引名
使用索引应注意的问题
使用以下内容会导致索引失效
使用不等于操作符(<>、!=)
使用is null 或 is not null
使用函数
比较不匹配的数据类型
适合建立索引的列
经常需要搜索的列
强制唯一性和组织表中数据的排列结构的列
经常用在连接的列上,这些列主要是一些外键,可以加快连接速度
经常需要根据范围进行搜索的列
经常需要排序的列,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间
经常使用在where子句中的列,可以加快条件判断的速度
不适合建立索引的列
在查询中很少使用或者参考的列不应该创建索引
只有很少数据值的列
定义为blob数据类型的列
修改性能远远大于检索性能时
注意增、删、修 改列,会降低索引性能
视图
视图是一个数据库中虚拟的表
经过查询操作形成的结果
具有普通表的结构
不能实现数据的存储
对视图的修改将会影响实际的数据表
创建视图
create or replace view 视图名 as select * from students;表示这个视图能查看到所有数据
从视图中检索数据
select * from 视图名
删除视图
drop view 视图名
PLSQL
什么是plsql
Pl/sql(Procedural Language)
一种过程化语言,通过增加编程语言的特点,实现对sql的扩展
过程化语言:指在sql命令语言中增加了过程处理语句(如分支、循环等),使sql语言具有过程处理能力.
Plsql的特点
支持所有的sql的语法
支持case语句,方便实现循环
通过继承,实现子类具有父类的属性和方法
设置了新的日期类型
PL/SQL语句块
DECLARE-- 声明变量,只能出现一次
NAME VARCHAR2(10);--变量
age NUMBER DEFAULT 0;-- 定义数字型变量,并赋给默认值0,如果不初始化值,就不能使用这个变量
sex CONSTANT VARCHAR2(20):='ss';-- 定义常量,并赋值
BEGIN
NAME:=’zhou’;-- 如果不边定义边赋值的话,只能在begin end语句块里面赋值
dbms_output.put_line(age+12+’d’);-- 字符串与数字不能相加,会发生异常
dbms_output.put_line(NAME);
EXCEPTION-- 处理异常
When others then
dbms_output.put_line('异常-----');-- 输出语句
END;
流程控制语句
IF-THEN-ELSIF语句
IF age=1 THEN dbms_output.put_line('11');
ELSIF age=2 THEN dbms_output.put_line('22');
ELSE dbms_output.put_line('elseelsele');
END IF;
CASE语句
CASE NAME
WHEN '周' THEN NAME:='雷';
WHEN 'b' THEN NAME:='清';
ELSE NAME:='丽';
END CASE;
LOOP循环
LOOP
age:=age+1;
IF age>=10 THEN EXIT;
END IF;
-- EXIT WHEN age>=10 ; 另一种退出循环方式
END LOOP;
WHILE-LOOP循环(相当于while循环)
WHILE age<10 LOOP
age:=age+1;
END LOOP;
FOR-LOOP循环(相当于for循环)
FOR age IN REVERSE 1..5 LOOP -- reverse表示反向循环 1..5表示从1开始5结束
dbms_output.put_line(age);
END LOOP;
动态SQL
什么是动态sql
编译期间sql语句是不确定的,并且在运行时允许发生变化
什么时候需要使用动态sql
需要增加程序的灵活性时
使用动态sql
DECLARE
plsql VARCHAR2(200);
t_id NUMBER:=1;
t_name VARCHAR2(20):='周小狗';
sVALUE VARCHAR2(40);
BEGIN
--EXECUTE IMMEDIATE 'create table temp_lei(id number,name varchar2(20))';创建表
-- plsql:='insert into temp_lei values(:1,:2)';-- 占位符
--EXECUTE IMMEDIATE plsql USING t_id,t_name;-- 把占位符替换成参数
plsql :='select name from temp_lei where id=1';
EXECUTE IMMEDIATE plsql INTO sVALUE;-- 把查询结果用into赋给svalue,只能接收一条语句
dbms_output.put_line(sVALUE);
END;
PL/SQL的异常处理
access_into_null | 视图给一个没有初始化的对象赋值 |
case_not_found | 在CASE语句中没有WHEN子句被选择,并且没有ELSE子句 |
invalid_number | 视图将一个非有效的字符串转换成数字 |
loggin_denied | 使用无效的用户名和口令登录Oracle |
no_data_found | 产寻语句无返回数据,或者引用了一个被删除的元素,或者引用了一个没有被初始化的元素 |
timeout_on_resource | Oracle在等待资源时发生超时的现象 |
处理异常
DECLARE
v_sql VARCHAR2(50);
NAME VARCHAR(20);
v_num NUMBER:=12;
BEGIN
CASE NAME
WHEN '周' THEN NAME:='雷';
WHEN 'b' THEN NAME:='清';
END CASE;
EXCEPTION -- exception语句块里只能用来处理异常,不能写逻辑代码
WHEN no_data_found THEN
dbms_output.put_line('不能使用空数据赋值');
WHEN case_not_found THEN
dbms_output.put_line('case when 没有else语句');
WHEN others THEN-- others必须放在最后,否则会报错
dbms_output.put_line('其他异常');
END;
自定义异常
DECLARE
v_exce EXCEPTION;-- 自定义异常
BEGIN
IF v_num=12 THEN
RAISE v_exce;-- 使用ralse触发异常
END ID;
EXCEPTION
WHEN v_exce THEN
dbms_output.put_line('自定义异常---');
END;
事务
事务的概念
事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列操作
多个操作作为一个整体向系统提交,事务是一个完整的操作,要么都执行,要么都不执行
事务是一个不可分割工作逻辑单元
转账过程就是一个事务
它需要两条update语句来完成,这两条语句是一个整体
如果其中任一条出现错误,则整个转账业务也应取消,两个账户中的余额应恢复到原来的数据,从而确保转账前和转账后的余额不变,即都是1001元
事务的特性
事务必须具备以下四个属性,简称ACID属性:
原子性(Atomicity)
事务是一个完整的操作,事务的各步操作是不可分的;要么都执行,要么都不执行
一致性(Consistency)
当事务完成时,数据必须处于一致状态
隔离性(Isolation)
并发事务之间彼此隔离、独立,它不应以任何方式依赖于或影响其他事务
永久性(Durability)
事务完成后,它对数据库的修改被永久保持
创建事务
BEGIN
UPDATE temp_lei SET money=money+100 WHERE ID=88;
UPDATE temp_lei SET money=money-100 WHERE ID=66;-- 当money小于1时就会发生异常
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('转账失败!');
ROLLBACK;-- 发生异常时回滚掉数据,所有操作都不执行,这样保证了数据的安全
END;
BEGIN
INSERT INTO temp_lei VALUES(11,'21',3);
INSERT INTO temp_lei VALUES(22,'22',3);
SAVEPOINT a;-- 设置事务回滚点
INSERT INTO temp_lei VALUES(444,'44',3);
INSERT INTO temp_lei VALUES(33,'23',-1);
EXCEPTION
WHEN OTHERS THEN
ROLLBACK TO a;-- 当发生异常时回滚到a,a后面的语句都不执行
END;
事务的隔离级别
数据异常:因为Oracle中支持多个事务并发执行,所以会出现下面的数据异常
脏读:事务T1更新了一行记录,还未提交所做的修改,这个T2读取了更新后的数据,然后T1执行回滚操作,取消刚才的修改,所以T2所读取的数据就无效,称为“错读”或者“脏读”
非重复读:事务t1读取一行记录,紧接着事务t2修改了t1刚刚读取的记录,然后t1再次查询,发现与第一次读取的记录不同,这称为不可重复读
幻读:事务t1读取一条指定where条件的语句,返回结果集,此时事务t2插入了一行新记录,恰好满足t1的where条件,然后t1使用相同的条件再次查询,结果集中可以看到t2插入的记录,这条新记录就是幻想
隔离级别 | 脏读 | 非重复读 | 幻读 |
Read uncommitted(非提交读) | 是 | 是 | 是 |
Read committed(提交读) | 否 | 是 | 是 |
Repeatable read(可重复读) | 否 | 否 | 是 |
Serializable(串行读) | 否 | 否 | 否 |
游标
游标的概念
游标用来存储用户通过一个查询语句得到的结果集,用户通过控制这个游标区域当中的指针来提前游标中的数据,然后进行操作,游标是存储在内存当中的
游标的分类
显示游标:需要用户手动创建
静态游标
动态游标
强类型ref游标
弱类型ref游标
隐式游标:dml 语句,select语句都会创建
控制显示静态游标
Fetch会抓取当前行记录,并将记录指针下移一行。注意:指针初始位置是指向第一行,并不是第一行前面
显示游标属性
通过使用游标属性,获取游标的状态信息
属性 | 类型 | 描述 |
%ISOPEN | Boolean | 游标打开,则返回True |
%NOTFOUND | Boolean | 如果最近抓取没有获得记录,返回True |
%FOUND | Boolean | 如果最近抓取获得记录,返回True |
%ROWCOUNT | Number | 返回到目前为止获取的记录数 |
静态游标
静态游标值的是程序执行的时候不需要再去解析sql语言,对于sql语句的解析在编译的时候就可以完成的。
DECLARE
CURSOR cur IS SELECT ID,NAME,money FROM temp_lei;-- 声明游标,cur是游标名
ID NUMBER;
NAME VARCHAR2(10);
money NUMBER;
--ID temp_lei.Id%TYPE; 直接使用表里该字段的类型,防止类型不匹配从而报错,建议使用这种方式定义变量
-- NAME temp_lei.name%TYPE;
--money temp_lei.money%TYPE;
BEGIN
OPEN cur;-- 打开游标
LOOP-- 由于fetch语句每次只返回一行数据,所以使用循环返回多条数据
FETCH cur INTO ID,NAME,money;-- 从游标获取数据,并把值赋给变量
注意:变量应该与游标字段个数相同,变量与字段顺序要一一对应,类型也要匹配
EXIT WHEN cur%NOTFOUND ;-- 如果没有取到数据就退出循环
dbms_output.put_line('id:'||ID||'name:'||NAME||' money:'||money);
END LOOP;
CLOSE cur;-- 关闭游标
END;
不论查询有没有返回记录,都不会引起一场
游标一旦打开,与之关联的select语句返回的结果集就被确定并固定下来了并传送到了游标工作区。
在游标声明中,SELECT子句不要包含INTO 子句,可以选出多行,或者0行。一旦在select子句中使用了into子句,则select必须选出一行,且只能选出一行
游标for循环
简化了显现游标的控制过程
不需要显示打开游标
不需要使用fetch来抓取数据
循环结束后不需要使用close来关闭游标
注意:游标都可以使用for循环,但只限于静态游标,用在其他里面都是错误的;for本身就包含了打开、关闭游标,此时再显示打开关闭都是错误的,但动态游标,必须要在打开时关联结果集,所以不可能省略打开游标这个动作
DECLARE
CURSOR cur IS SELECT * FROM temp_lei;-- for循环游标可以用*
ID temp_lei.Id%TYPE;
v_count cur%ROWTYPE;-- 返回所有记录行
BEGIN
FOR v_count IN cur LOOP
EXIT WHEN cur%NOTFOUND ;
ID:=v_count.ID;-- 可以给id赋值
dbms_output.put_line(v_count.ID||' name:'||v_count.NAME||ID);
END LOOP;
END;
%isopen属性使用方法
使用以下这种方式打开游标,可以提高性能
LOOP
IF cur%ISOPEN THEN -- 判断游标是否打开
FETCH cur INTO ID,NAME,money;-- 从游标获取数据
EXIT WHEN cur%NOTFOUND ;
dbms_output.put_line('id:'||ID||'name:'||NAME||' money:'||money||' 返回的记录数:'||cur%ROWCOUNT);
ELSE OPEN cur;-- 如果没打开的话,就打开
END IF;
END LOOP;
CLOSE cur;
带参数的游标
DECLARE
CURSOR cur(IDS NUMBER) IS SELECT ID,NAME FROM temp_lei WHERE ID=IDS;-- 查询传进来的ids的name和id
IDS NUMBER;
NAME VARCHAR2(20);
BEGIN
OPEN cur(11);-- 传参
LOOP
FETCH cur INTO IDS,NAME;
EXIT WHEN cur%NOTFOUND;
dbms_output.put_line('ID:'||IDS||' name:'||NAME);
END LOOP;
CLOSE cur;
END;
带参数的游标for循环
DECLARE
CURSOR cur(deptnos NUMBER) IS SELECT * FROM scott.dept where deptno=dnptnos;
v_count cur%ROWTYPE;-- 返回所有记录行
BEGIN
FOR v_count IN cur(10) LOOP
EXIT WHEN cur%NOTFOUND;
dbms_output.put_line('部门名称:'||v_count.dname);
END LOOP;
END;
动态游标
动态游标,可以在执行时,动态指定要指定的查询语句,动态游标又分为强类型ref游标和弱类型ref游标两种。
强类型ref游标:
有返回值类型,由于REF 游标变量的类型必须和return type一致,所以只能关联一张表
弱类型ref游标:
无返回值类型,可以关联多张表
什么是ref游标?
动态关联结果集的临时对象,即在运行时候动态决定执行查询。
Ref游标作用:实现程序间传递结果集的功能,利用ref游标也可以实现bulk sql,从而提高sql性能
REF游标变量是一种 引用 REF游标类型 的变量,指向动态关联的结果集。
强类型在使用时,其返回类型必须和return中的类型一致,否则报错,而弱类型可以随意打开任何类型。
强类型ref游标
declare
type emp_cur_type is ref cursor return scott.emp%rowtype;-- 指定返回值类型
emp_cur emp_cur_type;
emp_record scott.emp%rowtype;-- ②声明ref游标类型变量
begin
open emp_cur for select * from scott.emp;-- ③打开ref游标,关联结果集
loop
fetch emp_cur into emp_record;- ④获取记录,操作记录
exit when emp_cur%notfound;
dbms_output.put_line('name is:' || emp_record.ename || ' and sal is:' || emp_record.sal);
end loop;
close emp_cur;- ⑤关闭游标,释放资源
--open emp_cur for select * from scott.dept; --错误的,类型不一致。
--close emp_cur;
end;
弱类型ref游标
declare
type emp_cur_type is ref cursor;-- ①不指定返回值类型,能和任何类型的游标变量匹配,用于获取任何结果集
emp_cur emp_cur_type;
emp_record scott.emp%rowtype;-- ②声明ref游标类型变量
dept_record scott.dept%rowtype;
begin
open emp_cur for select * from scott.emp;-- ③打开ref游标,关联结果集
loop
fetch emp_cur into emp_record;-- ④获取记录,操作记录
exit when emp_cur%notfound;
dbms_output.put_line('name is:' || emp_record.ename || ' and sal is:' || emp_record.sal);
end loop;
close emp_cur;
open emp_cur for select * from scott.dept; --可再次打开,不同类型的
loop
fetch emp_cur into dept_record;
exit when emp_cur%notfound;
dbms_output.put_line('dname is----' || dept_record.dname);
end loop;
close emp_cur;-- ⑤关闭游标,释放资源
end;
静态游标和ref游标的区别
静态游标是静态定义,ref游标是动态关联
使用ref游标需要ref游标变量
ref游标能做为参数进行传递,而静态游标是不可能的
Pl/sql程序单元
是数据库中命名的pl/sql块,作为数据库对象保存在数据库里
主要有五类:
匿名块(最基本的pl/sql语句块):执行基本操作,无法保存。没有名称,不能被重复使用
过程:执行特定操作,无返回值
函数:进行复杂计算,有返回值
包:逻辑上相关的过程和函数组织在一起
触发器:事件触发,执行相应操作
函数和过程
Oracle提供可以把pl/sql程序存储在数据库中,并可以在任何地方来运行它,这样就叫存储过程或函数,过程和函数统称为pl/sql子程序,他们是被命名的pl/sql块,均存储在数据库中,并通过输入、输出参数或输入/输出参数与其调用者交换信息。过程和函数的唯一区别是函数总向调用者返回数据,而过程则不返回数据。
存储过程
在Oracle server上建立存储过程,可以被多个程序调用,可以向存储过程传递参数,也可以让存储过程传出参数。
CREATE OR REPLACE PROCEDURE WORK(ID NUMBER,id2 OUT NUMBER)-- 参数列表里的参数不需要指定长度,不指定输入(出)类型默认为in类型,in类型不能改变初始值,or replace 表示如果存在就替换
AS --这里也可以使用is
var NUMBER(20):=100;-- 在as里声明变量不需要用declare
BEGIN
id2:=ID;--为输出参数赋值
INSERT INTO scott.dept VALUES(ID,'lei','qing');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('插入失败:'||SQLERRM); --打印错误信息:SQLERRM
END;
调用:
如果不放在语句块中调用,语法是:call 过程名();
如果过程有out/in out参数应该放在语句块中调用,因为要定义变量接收输出/输入输出参数的返回值
DECLARE
num NUMBER:=0;
BEGIN
WORK(50,num);-- 在语句块里调用不需要call
dbms_output.put_line(num);
END;
存储过程:基本规则
在as/is(相当于declare)里声明类型和变量
创建无参的过程不需要括号
存储过程有in、out、in out参数
参数模式
IN | OUT | IN OUT |
默认 | 必须指定 | 必须指定 |
值被:传递给子程序。子程序不能改变参数值。 | 值被:返回调用环境。子程序会改变参数值。 | 值被:传递给子程序,返回调用环境。子程序可以改变参数值。 |
可以是表达式,常量、或者是有值的变量 | 必须是一个变量,这个变量是否有值不重要。 | 必须是一个变量,而且变量应该有值。 |
函数
函数用来执行复杂的计算,并返回计算的结果
在pl/sql块中至少包含一个有效的return语句
CREATE OR REPLACE FUNCTION fun1(num NUMBER)--只能使用in模式参数传入参数值
RETURN VARCHAR2-- 返回值类型
IS
var VARCHAR2(30);
BEGIN
IF num>100 THEN var:='111';
ELSIF num>50 THEN var:='222';
ELSIF num>20 THEN var:='333';
ELSE var:='else--else';
END IF;
RETURN var;-- 返回值必须和返回值类型一致
END;
调用
--------------------------------调用方式一
DECLARE
aa NUMBER:=0;
BEGIN
SELECT fun1(44) INTO aa FROM dual;
dbms_output.put_line(aa);
END;
--------------------------------调用方式二
SELECT fun1(44) FROM dual;
参数默认值
具有默认值的函数创建后,在函数调用时,如果没有为具有默认值的参数提供实际参数值,函数将使用该参数的默认值,但当调用者为默认参数提供实际参数时,函数将使用实际参数值,在创建函数时,只能为输入参数设置默认值,而不能为输入/输出参数设置默认值
CREATE OR REPLACE FUNCTION demo_fun(Sex VARCHAR2 DEFAULT '男')
RETURN VARCHAR2
IS
V_var VARCHAR2(32);
BEGIN
V_var := name||':'||TO_CHAR(age)||'岁,'||sex;
RETURN v_var;
END;
函数的要求
函数与过程语法差异:定义函数header时比过程多一个retur声明,在函数执行体中必须有一个有效的return语句
只能使用in模式参数传入参数值
在sql语句中调用函数
好处
可以实现用简单sql语句不能实现的计算
提高查询的效率
使用规则
只能使用函数,而不是过程
形参必须为in模式
存储过程和函数的对比
存储过程 | 函数 |
作为独立的PL/SQL语句块来执行,完成特定的业务功能 | 作为表达式的部分被调用,完成一次特定的计算。 |
使用PROCEDURE说明 | 使用FUNCTION说明 |
不需返回类型声明 | 必须声明函数返回值类型 |
不需RETURN语句 | 程序主体必须包含有效的RETURN语句 |
可以使用IN/OUT/IN OUT的传参模式 | 只能使用IN的传参模式 |
可以不返回值,可以返回一个或多个值(并不是显式的返回值),通过OUT/IN OUT模式参数返回 | 必须,且只能返回一个值, 返回值类型必须是Oracle SQL支持的数据类型。通过RETURN语句返回 |
删除过程和函数
删除过程:
DROP PROCEDURE proceduer_name;
删除函数:
DROP FUNCTION function_name;
包
包是一组相关过程、函数、变量、常量、和游标等pl/sql程序设计元素的组合,它具有面向对象程序设计语言的特点,是对这些pl/sql程序元素的封装。包类似与c++和java语言中的类,其中变量相当于类中的成员变量,过程和函数相当于类方法。把相关的程序单元归纳到包里。
使用包可以提高效率,因为,当程序首次调用包内函数或过程时,oracle将整个包调入内存,当再次访问包内元素时,oracle直接从内存中读取,而不需要进行磁盘操作,从而使程序执行效率得到提高。
包的结构
一个包由两个分开的部分组成:
包头(package):包头部分声明包内数据类型、变量、常量、游标、子程序和异常错误处理等元素,这些元素为包的公有元素
包主体(package body):包主体则是包定义部分的具体实现,它负责为包头中所声明子程序提供具体的实现,在包主体中还可以声明包的私有元素
包头和包主体分开编译,并作为两个分开的对象分别存放在数据库字典中
其实包头就相当于接口中的定义行为,包体就相当于实现这个接口,并重写方法。
可以只定义包头而不定义包体
使用包和不使用包的语法差异
使用包来定义函数和过程时,要先在包头里声明函数和过程。否则,这些函数和过程将只能在该包内有效。
可以这样理解:包头里定义的成分是public的,离开包之后还可以访问,但包体里定义的成分都是private的,离开包之后将不可以访问.
在包体里定义函数和过程时,无需使用create or replace 关键字。
包头定义
包头主要包含了包中出现的过程、函数的声明,但是不包括实际的代码部分
CREATE OR REPLACE PACKAGE pack_name-- 包头,必须先编译包头,才能编译包体,包头名和包体名要一致
IS
v_num NUMBER:=0;-- 声明变量
PROCEDURE pro_1(num NUMBER);-- 声明过程
FUNCTION fun_2(var VARCHAR2) RETURN VARCHAR2;-- 声明函数
type emp_cur_type is ref cursor return scott.emp%rowtype;--声明ref强类型游标
END pack_name;
包体
包体是一个独立于包头的数据库对象
包体只能在包头完成编译后才能进行编译
过程和函数的实现应该放在声明部分
CREATE OR REPLACE PACKAGE BODY pack_name-- 包体
IS
v VARCHAR2(10);
PROCEDURE pro_1(num NUMBER)-- 过程实现,注意:只要在包头里声明了函数或过程就必须在包体里实现,而且只能放在包体的声明部分
IS
BEGIN
SELECT fun_2('13')INTO v FROM dual;-- 在同一个包中直接调用
dbms_output.put_line(num);
END;
----
FUNCTION fun_2(var VARCHAR2)-- 函数实现
RETURN VARCHAR2
IS
BEGIN
--dbms_output.put_line(var);
RETURN var;
END;
BEGIN
v_num:=22;
END pack_name;
调用
如果在包内调用的话,直接用函数名或过程名即可
在包外调用要用包名.过程名
DECLARE
a NUMBER:=0;
c VARCHAR2(40);
BEGIN
a:=pack_name.v_num;-- 调用包中的变量
SELECT pack_name.fun_2('zhou')INTO c FROM dual;-- 调用包中的函数
pack_name.pro_1(212);-- 调用包中的过程
dbms_output.put_line(a||c);
END;
包体里的执行语句
包体里也可以有执行语句。
包体的执行语句放在包体的begin里面
当执行包里的过程时,包体所包含的执行语句会获得执行的机会,而且是在过程之前获得执行
包的基本规则
在包头理声明过的过程、函数、变量、常量、类型是全局有效的。(当前Schema所有地方)。
在包体内也可以重新定义过程、函数、变量,但这些定义只能在包体内访问,相当于私有的。
包体里定义的变量不要与包头里变量的同名。
在包中声明的时候,必须是前向声明的,也就是对象需要引用的另一个对象必须在前面首先声明
包头是必须存在的,包体是可选的
包头必须在包体前创建,否则创建不了包体
只在包头对公用变量、过程、函数作声明。
使用包的好处
可以把相关的过程、函数组织在一起,便于管理
提高了代码性能
能够将私有的过程、函数隐藏起来,只在包体内部看得到,提高了安全性
包头中变量可作为全局变量所用,在一个Schema中能够一直保持,提供了全局变量的作用。
删除包
DROP PACKAGE 包名;
触发器
触发器在数据库里一独立的对象存储,它与存储过程不同的是,存储过程通过其它程序来启动运行或直接启动运行,而触发器是有一个事件来启动运行。
触发器无需手动调用,当某个事件发生时会自动地隐式运行。触发器不能接收参数,所以运行触发器就叫触发或点火
触发器作用
安全性方面
产生对数据值修改的审计
提供更灵活的完整性校验规则
提供表数据的同步复制
事件日志记录
触发器组成
触发事件:(insert,update,delete)
触发时间:(before,after)
触发器本身:即触发器被触发之后的执行体(语句块)
触发频率:触发器内定义的动作被执行的次数(分为行级和语句级)
语句级触发器:是指当触发事件发生时,该触发器只执行一次。
行级触发器:是指当某触发事件发生时,对受到该操作影响的每一行数据,触发器都单独执行一次。
触发器类型
DML触发器 :
DML触发器类型主要包括INSERT,UPDATE,DELETE三种触发器
操作对象:表或者视图
触发的时机包括:对表来说有before或after触发,对视图来说有INSTEAD OF
可以设置WHEN子句,决定触发后是否执行触发器的执行部分
系统触发器:
可以在ORACLE数据库系统的事件中进行触发,如执行DDL或ORACLE系统的启动与关闭等系统事件。
创建DML触发器
语句级触发器
CREATE OR REPLACE TRIGGER my_trig after INSERT ON student
BEGIN
dbms_output.put_line('您插入了一条数据---');
END;
行级触发器
CREATE OR REPLACE TRIGGER my_trig2 BEFORE DELETE ON student-- old 和new只能使用在行级触发器内
FOR EACH ROW
BEGIN
dbms_output.put_line('您开始删数据了');
IF (:OLD.sno)='s011' THEN dbms_output.put_line('不允许删这条数据!');
ELSE
dbms_output.put_line('删除成功!!');
END IF;
END;
带条件的行级触发器
只有行级触发器才能使用when子句
CREATE OR REPLACE TRIGGER del_emp
BEFORE update ON student
REFERENCING new AS nn old AS oo -- 给new和old取别名
FOR EACH ROW
WHEN (oo.sage > 22)-- 旧的年龄大于22才会执行begin里面的代码
BEGIN
dbms_output.put_line(:nn.sage||'------'||:oo.sage);
END;
行级触发器中的相关标识符
只有行级触发器才能使用old和new
特性 | INSERT | UPDATE | DELETE |
:OLD | NULL | 有效 | 有效 |
:NEW | 有效 | 有效 | NULL |
每张表最多能创建12种触发器
比如说:insert事件
有触发时间为before的语句级触发器
有触发时间为after的语句级触发器
有触发时间为before的行级触发器
有触发时间为after的行级触发器
使用触发器的限制
CREATE TRIGGER语句文本的字符长度不能超过32KB
触发器体内的SELECT 语句只能为SELECT … INTO …结构,或者为定义游标所使用的SELECT 语句
触发器中不能使用数据库事务控制语句 COMMIT, ROLLBACK, SVAEPOINT 语句
由触发器所调用的过程或函数也不能使用数据库事务控制语句
触发器中不能使用LONG, LONG RAW 类型
创建系统触发器
系统触发器可以在DDL语句或数据库系统事件来触发。
DDL指的是数据定义语言,如CREATE 、ALTER及DROP 等。
数据库系统事件包括数据库服务器的启动或关闭,用户的登录与退出、数据库服务错误等
CREATE OR REPLACE TRIGGER my_create
AFTER CREATE ON database
BEGIN
dbms_output.put_line('您创建了一张表!');
END;
系统触发器的注意点
系统触发器只能建立在一个Schema或database上
当建立在schema上时,只有模式所对应用户的ddl操作和他们所导致的事件才激活触发器,默认为当前用户模式。
当建立在database上时,该数据库所有的用户的ddl操作和他们所导致的错误,以及数据库的启动和关闭均可激活触发器
要在数据上建立触发器时,要求用户具有administer database trigger权限
系统触发器的事件和允许时机
事件 | 允许的时机 | 说明 |
启动(STARTUP) | 之后 | 实例启动时激活 |
关闭(SHUTDOWN) | 之前 | 实例正常关闭时激活 |
服务器错误(SERVERERROR) | 之后 | 只要有错误就激活 |
登录(LOGON) | 之后 | 成功登录后激活 |
注销(LOGOFF) | 之前 | 开始注销时激活 |
创建(CREATE) | 之前,之后 | 在创建之前或之后激活 |
删除(DROP) | 之前,之后 | 在撤消之前或之后激活 |
修改(ALTER) | 之前,之后 | 在变更之前或之后激活 |
删除触发器
DROP TRIGGER 触发器名;
当删除其他用户Schema中的触发器时,需要具有DROP ANY TRIGGER系统权限,当删除建立在数据库上的触发器时,用户需要具有ADMINISTER DATABASE TRIGGER系统权限
此外,当删除表或视图时,建立在这些对象上的触发器也随之删除
管理触发器
启用或者禁用某个触发器
ALTER TRIGGER trigger_name DISABLE | ENABLE
启用或者禁用某个对象上的所有触发器
ALTER TABLE table_name DISABLE |
ENABLE ALL TRIGGERS
重编译触发器
ALTER TRIGGER trigger_name COMPILE