本文基于黑马程序员文档做的二次总结,如有侵权,请联系本人删除。
文章目录
字段定义
创建表空间
create tablespace waterboss
datafile 'c:\waterboss.dbf'
size 100m
autoextend on
next 10m;
waterboss 为表空间名称
datafile 用于设置物理文件名称
size 用于设置表空间的初始大小
autoextend on 用于设置自动增长,如果存储量超过初始大小,则开始自动扩容
next 用于设置扩容的空间大小
创建用户
create user wateruser
identified by itcast
default tablespace waterboss;
wateruser 为创建的用户名
identified by 用于设置用户的密码
default tablesapce 用于指定默认表空间名称
用户赋权
grant dba to wateruser
DBA: 拥有全部特权,是系统最高权限,只有 DBA 才可以创建数据库结构。
RESOURCE:拥有 Resource 权限的用户只可以创建实体,不可以创建数据库结构。
CONNECT:拥有 Connect 权限的用户只可以登录 Oracle,不可以创建实体,不可以创建数据库结构。
对于普通用户:授予 connect, resource 权限。
对于 DBA 管理用户:授予 connect,resource, dba 权限。
创建表
create table t_owners
(
id number primary key,
name varchar2(30),
addressid number,
housenumber varchar2(30),
watermeter varchar2(30),
adddate date,
ownertypeid number
);
数据类型:
- 字符型
(1)CHAR : 固定长度的字符类型,最多存储 2000 个字节
(2)VARCHAR2 :可变长度的字符类型,最多存储 4000 个字节(3)LONG : 大文本类型。最大可以存储 2 个 G
2.数值型
NUMBER : 数值类型
例如:NUMBER(5) 最大可以存的数为 99999
NUMBER(5,2) 最大可以存的数为 999.99
3.日期型
(1)DATE:日期时间型,精确到秒
(2)TIMESTAMP:精确到秒的小数点后 9 位
4.二进制型(大数据类型)
(1)CLOB : 存储字符,最大可以存 4 个 G
(2)BLOB:存储图像、声音、视频等二进制数据,最多可以存 4 个 G
表增加字段
--追加字段
ALTER TABLE T_OWNERS ADD
(
REMARK VARCHAR2(20),
OUTDATE DATE
);
表更改字段类型
ALTER TABLE T_OWNERS MODIFY
(
REMARK CHAR(20) ,
OUTDATE TIMESTAMP
);
表字段重命名
ALTER TABLE T_OWNERS RENAME COLUMN OUTDATE TO EXITDATE;
表删除字段
# ALTER TABLE T_OWNERS DROP COLUMN REMARK;
ALTER TABLE T_OWNERS DROP (REMARK,EXITDATE);
删除表
# DROP TABLE T_OWNERS;
记录的增删改
表插入记录
insert into T_OWNERS VALUES (1,' 张三丰',1,'2-2','5678',sysdate,1);
insert into T_OWNERS (id ,name,addressid,housenumber,watermeter ,adddate,ownertypeid) VALUES (2,'赵大侃',1,'2-3','9876',sysdate,1);
commit;
表修改记录
update T_OWNERS set adddate=adddate-3 where id=1;
commit;
表删除记录
delete from T_OWNERS where id=2;
commit;
# TRUNCATE TABLE T_OWNERS
比较 truncat 与 delete 实现数据删除?
- delete 删除的数据可以 rollback
- delete 删除可能产生碎片,并且不释放空间
- truncate 是先摧毁表结构,再重构表结构
导入导出数据库
导出数据库全部数据
exp system/itcast full=y;
# exp system/itcast file=文件名 full=y
不指定 file 参数,则默认用导出 EXPDAT.DMP
导入数据库全部数据
imp system/itcast full=y;
# imp system/itcast full=y file=water.dmp;
按用户导入数据库
exp system/itcast owner=wateruser file=wateruser.dmp;
按用户导入数据库
imp system/itcast file=wateruser.dmp fromuser=wateruser;
按表导出数据库
exp wateruser/itcast file=a.dmp tables=t_account,a_area;
按表导入数据库
imp wateruser/itcast file=a.dmp tables=t_account,a_area;
数据查询
select distinct name from t_owners where (name like '%刘%' or usenum<=20000
') and addressid=3 and maxnum is not null order by usenum desc
精确查询
select * from T_OWNERS where watermeter='30408'
模糊查询
select * from t_owners where name like '%刘%'
and or 混用
select * from t_owners where (name like '%刘%' or housenumber like '%5%') and addressid=3
范围查询
select * from T_ACCOUNT where usenum>=10000 and usenum<=20000;
-- select * from T_ACCOUNT where usenum between 10000 and 20000
不建议使用between,再别的sql中有不含等于号的情况。
空值查询
select * from T_PRICETABLE t where maxnum is not null
-- select * from T_PRICETABLE t where maxnum is null
去重复查询
select distinct addressid from T_OWNERS
升序查询
select * from T_ACCOUNT order by usenum
降序查询
select * from T_ACCOUNT order by usenum desc
伪列ROWID查询
-- select rowID,t.* from T_AREA t
select rowID,t.* from T_AREA t where ROWID='AAAM1uAAGAAAAD8AAC';
伪列ROWNUM查询
-- select rowID,rownum,t.* from T_AREA t
select rownum,t.* from T_OWNERTYPE t
聚合查询
聚合查询求和
select sum(usenum) from t_account where year='2012'
聚合查询求平均
select avg(usenum) from T_ACCOUNT where year='2012'
聚合查询求最大值
select max(usenum) from T_ACCOUNT where year='2012'
聚合查询求最小值
select min(usenum) from T_ACCOUNT
聚合查询统计个数
select areaid,sum(money) from t_account group by areaid
分组后条件查询
select areaid,sum(money) from t_account group by areaid having sum(money)>169000
多表内连接查询
select o.id 业主编号,o.name 业主名称,ot.name 业主类型
from T_OWNERS o,T_OWNERTYPE ot
where o.ownertypeid=ot.id
select o.id 业主编号,o.name 业主名称,ad.name 地址,
ot.name 业主类型
from T_OWNERS o,T_OWNERTYPE ot,T_ADDRESS ad
where o.ownertypeid=ot.id and o.addressid=ad.id
多表左外连接查询
SELECT ow.id,ow.name,ac.year ,ac.month,ac.money
FROM T_OWNERS ow left join T_ACCOUNT ac
on ow.id=ac.owneruuid
ORACLE语法优化,在右表所在的条件一端填上(+)
SELECT ow.id,ow.name,ac.year ,ac.month,ac.money FROM
T_OWNERS ow,T_ACCOUNT ac
WHERE ow.id=ac.owneruuid(+)
多表右外连接查询
select ow.id,ow.name,ac.year,ac.month,ac.money from
T_OWNERS ow right join T_ACCOUNT ac on ow.id=ac.owneruuid
ORACLE 的语法优化,在左表所在的条件一端填上(+)
select ow.id,ow.name,ac.year,ac.month,ac.money from
T_OWNERS ow , T_ACCOUNT ac where ow.id(+) =ac.owneruuid
子查询
where 子句中的子查询
select * from T_ACCOUNT
where year='2012' and month='01' and usenum >
( select avg(usenum) from T_ACCOUNT where year='2012' and month='01' )
多行子查询 in
select * from T_OWNERS where addressid in ( 1,3,4 )
select * from T_OWNERS where addressid not in
( select id from t_address where name like '%花园%' )
多行子查询 any
多行子查询 all
from 子句中的子查询
select * from (select o.id 业主编号,o.name 业主名称,ot.name 业主类型
from T_OWNERS o,T_OWNERTYPE ot
where o.ownertypeid=ot.id)
where 业主类型='居民'
select 子句中的子查询
select id,name, (select name from t_address where id=addressid) addressname
from t_owners
分页查询
rownum分页查询
select * from
(select rownum r,t.* from T_ACCOUNT t where rownum<=20)
where r>10
这是因为 rownum 是在查询语句扫描每条记录时产生的,所以不能使用“大于”
符号,只能使用“小于”或“小于等于” ,只用“等于”也不行。
rownum分页查询且排序
select * from
(select rownum r,t.* from
(select * from T_ACCOUNT order by usenum desc) t
where rownum<=20 )
where r>10
对结果先排序,再根据查询结果表进行分页。原因分析 ROWNUM 伪列的产生是在表记录扫描
是产生的,而排序是后进行的,排序时 R 已经产生了,所以排序后 R 是乱的。
row_number() over分页查询且排序
select * from
(select row_number() over(order by usenum desc )
rownumber,usenum from T_ACCOUNT)
where rownumber>10
分页查询minus
select rownum,t.* from T_ACCOUNT t where rownum<=20
minus
select rownum,t.* from T_ACCOUNT t where rownum<=10
字符函数
ASCII 返回对应字符的十进制值
CHR 给出十进制返回字符
CONCAT 拼接两个字符串,与 || 相同
INITCAT 将字符串的第一个字母变为大写
INSTR 找出某个字符串的位置
INSTRB 找出某个字符串的位置和字节数
LENGTH 以字符给出字符串的长度
LENGTHB 以字节给出字符串的长度
LOWER 将字符串转换成小写
LPAD 使用指定的字符在字符的左边填充
LTRIM 在左边裁剪掉指定的字符
RPAD 使用指定的字符在字符的右边填充
RTRIM 在右边裁剪掉指定的字符
REPLACE 执行字符串搜索和替换
SUBSTR 取字符串的子串
SUBSTRB 取字符串的子串(以字节)
SOUNDEX 返回一个同音字符串
字符串长度length
select length('ABCD') from dual;
字符串截取substr
select substr('ABCD',2,2) from dual; -- BC
从第几位开始截取,截取多少位
字符串拼接concat ||
select concat('ABC','D') from dual;
select 'ABC'||'D' from dual;
数值函数
ABS(value) 绝对值
CEIL(value) 大于或等于 value 的最小整数
COS(value) 余弦
COSH(value) 反余弦
EXP(value) e 的 value 次幂
FLOOR(value) 小于或等于 value 的最大整数
LN(value) value 的自然对数
LOG(value) value 的以 10 为底的对数
MOD(value,divisor) 求模
POWER(value,exponent) value 的 exponent 次幂
ROUND(value,precision) 按 precision 精度 4 舍 5 入
SIGN(value) value 为正返回 1;为负返回-1;为 0 返回 0. SIN(value) 余弦
SINH(value) 反余弦
SQRT(value) value 的平方根
TAN(value) 正切
TANH(value) 反正切
TRUNC(value,按 precision) 按照 precision 截取 value
VSIZE(value) 返回 value 在 ORACLE 的存储空间大小
四舍五入
select round(100.567) from d
select round(100.567,2) from dual
截取函数
select trunc(100.567) from dual
select trunc(100.567,2) from dual
求余函数 (取模)
select mod(10,3) from dual
日期函数
ADD_MONTHS 在日期 date 上增加 count 个月
GREATEST(date1,date2,. . .) 从日期列表中选出最晚的日期
LAST_DAY( date ) 返回日期 date 所在月的最后一天
LEAST( date1, date2, . . .) 从日期列表中选出最早的日期
MONTHS_BETWEEN(date2, date1) 给出 Date2 - date1 的月数(可以是小数)
NEXT_DAY( date,’day’) 给出日期 date 之后下一天的日期,这里的 day 为星期,
如: MONDAY,Tuesday 等。
NEW_TIME(date,’this’,’other’) 给出在 this 时区=Other 时区的日期和时间
ROUND(date,’format’) 未指定 format 时,如果日期中的时间在中午之前,则
将日期中的时间截断为 12 A.M.(午夜,一天的开始),否则进到第二天。时间截断为 12 A.M.(午夜,一天的开始), 否则进到第二天。
TRUNC(date,’format’) 未指定 format 时,将日期截为 12 A.M.( 午夜,一天的开始
获取当前时间
select sysdate from dual
增加月份
select add_months(sysdate,2) from dual
所在月最后一天
select last_day(sysdate) from dual
日期截取到零时
select TRUNC(sysdate) from dual
日期截取为1月
select TRUNC(sysdate,'yyyy') from dual
日期截取到零秒
select TRUNC(sysdate,'mm') from dual
类型转换函数
CHARTOROWID 将 字符转换到 rowid 类型
CONVERT 转换一个字符节到另外一个字符节
HEXTORAW 转换十六进制到 raw 类
RAWTOHEX 转换 raw 到十六进制
ROWIDTOCHAR 转换 ROWID 到字符
TO_CHAR 转换日期格式到字符串
TO_DATE 按照指定的格式将字符串转换到日期型
TO_MULTIBYTE 把单字节字符转换到多字节
TO_NUMBER 将数字字串转换到数字
TO_SINGLE_BYTE 转换多字节到单字节
数字转字符串 to_char
select TO_CHAR(1024) from dual
日期转字符串 to_char
select TO_CHAR(sysdate,'yyyy-mm-dd') from dual
select TO_CHAR(sysdate,'yyyy-mm-dd hh:mi:ss') from dual
字符串转日期
select TO_DATE('2017-01-01','yyyy-mm-dd') from dual
指定值转换函数nvl
select NVL(NULL,0) from dual
select PRICE,MINNUM,NVL(MAXNUM,9999999)
from T_PRICETABLE where OWNERTYPEID=1
null值判断转换函数
select PRICE,MINNUM,NVL2(MAXNUM,to_char(MAXNUM) , '不限')
from T_PRICETABLE where OWNERTYPEID=1
条件取值 decode
select name,decode( ownertypeid,1,' 居 民 ',2,' 行 政 事 业 单 位',3,'商业') as 类型 from T_OWNERS
条件取值 case when then
select name ,(case ownertypeid
when 1 then '居民'
when 2 then '行政事业单位'
when 3 then '商业'
else '其它'
end
) from T_OWNERS
select name,(case
when ownertypeid= 1 then '居民'
when ownertypeid= 2 then '行政事业'
when ownertypeid= 3 then '商业'
end )
行列转换
select (select name from T_AREA where id= areaid ) 区域,
sum( case when month='01' then money else 0 end) 一月,
sum( case when month='02' then money else 0 end) 二月,
sum( case when month='03' then money else 0 end) 三月,
sum( case when month='04' then money else 0 end) 四月,
sum( case when month='05' then money else 0 end) 五月,
sum( case when month='06' then money else 0 end) 六月,
sum( case when month='07' then money else 0 end) 七月,
sum( case when month='08' then money else 0 end) 八月,
sum( case when month='09' then money else 0 end) 九月,
sum( case when month='10' then money else 0 end) 十月,
sum( case when month='11' then money else 0 end) 十一月,
sum( case when month='12' then money else 0 end) 十二月
from T_ACCOUNT where year='2012' group by areaid
select (select name from T_AREA where id= areaid ) 区域,
sum( case when month>='01' and month<='03' then money else
0 end) 第一季度,
sum( case when month>='04' and month<='06' then money else
0 end) 第二季度,
sum( case when month>='07' and month<='09' then money else
0 end) 第三季度,
sum( case when month>='10' and month<='12' then money else
0 end) 第四季度
from T_ACCOUNT where year='2012' group by areaid
分析函数
相同的值排名相同,排名跳跃 rank() over
select rank() over(order by usenum desc ),usenum from T_ACCOUNT
相同的值排名相同,排名连续 DENSE_RANK
select dense_rank() over(order by usenum desc ),usenum from T_ACCOUNT
ROW_NUMBER 返回连续的排名,无论值是否相等
select row_number() over(order by usenum desc ),usenum from T_ACCOUNT
row_number() over实现分页查询
select * from
(select row_number() over(order by usenum desc )
rownumber,usenum from T_ACCOUNT)
where rownumber>10
集合运算
允许元素重复的集合相加 union all
select * from t_owners where id<=7
union all
select * from t_owners where id>=5
不允许元素重复的合集相加 union
select * from t_owners where id<=7
union
select * from t_owners where id>=5
求集合相交的元素intersect
select * from t_owners where id<=7
intersect
select * from t_owners where id>=5
求一个集合减去相交部分 minus
select * from t_owners where id<=7
minus
select * from t_owners where id>=5
分页查询minus
select rownum,t.* from T_ACCOUNT t where rownum<=20
minus
select rownum,t.* from T_ACCOUNT t where rownum<=10
视图
OR REPLACE :若所创建的试图已经存在,ORACLE 自动重建该视图;
FORCE :不管基表是否存在 ORACLE 都会自动创建该视图;
subquery :一条完整的 SELECT 语句,可以在该语句中定义别名;
WITH CHECK OPTION :插入或修改的数据行必须满足视图定义的约束;
WITH READ ONLY :该视图上不能进行任何 DML 操作。
创建或修改视图
create or replace view view_address2 as
select * from T_ADDRESS where areaid=2
带约束的视图with check option
只能修改视图中显示的数值
create or replace view view_address2 as
select * from T_ADDRESS where areaid=2
with check option
创建错误视图
create or replace FORCE view view_TEMP as
select * from T_TEMP
创建视图时T_TEMP表不存在
多表关联视图
create or replace view view_owners as
select o.id 业主编号,o.name 业主名称,ot.name 业主类型
from T_OWNERS o,T_OWNERTYPE ot
where o.ownertypeid=ot.id
多表关联视图键保留表的列
update view_owners set 业主名称='范小冰' where 业主编号=1; --修改成功,视图中o主键是唯一且非空
update view_owners set 业主类型='普通居民' where 业主编号=1; --修改失败,视图中ot主键不是唯一且非空
多表关键聚合查询视图
create view view_accountsum as
select year,month,sum(money) moneysum
from T_ACCOUNT
group by year,month
order by year,month
此例用到聚合函数,没有键保留表,所以视图无法执行 update 。
物化视图
CREATE METERIALIZED VIEW view_name
[BUILD IMMEDIATE | BUILD DEFERRED ]
REFRESH [FAST|COMPLETE|FORCE]
[
ON [COMMIT |DEMAND ] | START WITH (start_time) NEXT
(next_time)
]
AS
subquery
BUILD IMMEDIATE 是在创建物化视图的时候就生成数据BUILD DEFERRED 则在创建时不生成数据,以后根据需要再生成数据。默认为 BUILD IMMEDIATE。
刷新(REFRESH):指当基表发生了 DML 操作后,物化视图何时采用哪种方式和基表进行同步。
REFRESH 后跟着指定的刷新方法有三种:FAST、COMPLETE、FORCE。FAST刷新采用增量刷新,只刷新自上次刷新以后进行的修改。COMPLETE 刷新对整个物化视图进行完全的刷新。如果选择 FORCE 方式,则 Oracle 在刷新时会去判断是否可以进行快速刷新,如果可以则采用 FAST 方式,否则采用 COMPLETE的方式。FORCE 是默认的方式。
刷新的模式有两种:ON DEMAND 和 ON COMMIT。ON DEMAND 指需要手动刷新物化视图(默认)。ON COMMIT 指在基表发生 COMMIT 操作时自动刷新。
物化视图手动刷新
create materialized view mv_address
as
select ad.id,ad.name adname,ar.name ar_name
from t_address ad,t_area ar
where ad.areaid=ar.id
begin
DBMS_MVIEW.refresh('MV_ADDRESS','C');
end;
EXEC DBMS_MVIEW.refresh('MV_ADDRESS','C'); --注意:此语句需要在命令窗口中执行。
物化视图自动刷新
create materialized view mv_address2
refresh on commit
as
select ad.id,ad.name adname,ar.name ar_name
from t_address ad,t_area ar
where ad.areaid=ar.id
物化视图自动刷新-创建时不生成数据
create materialized view mv_address3
build deferred
refresh
on commit
as
select ad.id,ad.name adname,ar.name ar_name
from t_address ad,t_area ar
where ad.areaid=ar.id;
执行手动刷新或插入,才刷入数据
begin
DBMS_MVIEW.refresh('MV_ADDRESS3','C');
end;
insert into t_address values(8,'宏福苑小区',1,1);
物化视图增量刷新
create materialized view log on t_address with rowid;
create materialized view log on t_area with rowid
先创建物化视图日志,创建的物化视图日志自动命名为 MLOG
表
名称。
S
N
A
P
T
I
M
E
_表名称。 SNAPTIME
表名称。SNAPTIME
:用于表示刷新时间。
D
M
L
T
Y
P
E
:用于表示刷新时间。 DMLTYPE
:用于表示刷新时间。DMLTYPE
:用于表示
D
M
L
操作类型,
I
表示
I
N
S
E
R
T
,
D
表示
D
E
L
E
T
E
,
U
表示
U
P
D
A
T
E
。
O
L
D
N
E
W
:用于表示 DML 操作类型,I 表示 INSERT,D 表示 DELETE,U表示 UPDATE。 OLD_NEW
:用于表示DML操作类型,I表示INSERT,D表示DELETE,U表示UPDATE。OLDNEW
:用于表示这个值是新值还是旧值。
N
(
E
W
)表示新值,
O
(
L
D
)表示旧值,
U
表示
U
P
D
A
T
E
操作。
C
H
A
N
G
E
V
E
C
T
O
R
:用于表示这个值是新值还是旧值。N(EW)表示新值,O(LD) 表示旧值,U 表示 UPDATE 操作。 CHANGE_VECTOR
:用于表示这个值是新值还是旧值。N(EW)表示新值,O(LD)表示旧值,U表示UPDATE操作。CHANGEVECTOR$:表示修改矢量,用来表示被修改的是哪个或哪几个字段。
此列是 RAW 类型,其实 Oracle 采用的方式就是用每个 BIT 位去映射一个列。
插入操作显示为:FE, 删除显示为:OO 更新操作则根据更新字段的位置而显示
不同的值。
create materialized view mv_address4
refresh fast
as
select ad.rowid adrowid ,ar.rowid arrowid, ad.id,ad.name
adname,ar.name ar_name
from t_address ad,t_area ar
where ad.areaid=ar.id;
在查询语句中,必须包含所有表的 rowid ( 以 rowid 方式建立物化视图日志 )
begin
DBMS_MVIEW.refresh('MV_ADDRESS4','C');
end;
当我们手动刷新物化视图后,物化视图日志被清空,物化视图更新。
序列
创建序列
CREATE SEQUENCE sequence //创建序列名称
[INCREMENT BY n] //递增的序列值是 n 如果 n 是正数就递增,如果是负数就递减 默
认是 1
[START WITH n] //开始的值,递增默认是 minvalue 递减是 maxvalue
[{MAXVALUE n | NOMAXVALUE}] //最大值
[{MINVALUE n | NOMINVALUE}] //最小值
[{CYCLE | NOCYCLE}] //循环/不循环
[{CACHE n | NOCACHE}];//分配并存入到内存中
create sequence 序列名称
获取序列当前值
select 序列名称.currval from dual
获取序列下一值
select 序列名称.nextval from dual
每次获取下一值导致数值序列递进
创建有最大值非循环序列
create sequence seq_test1
increment by 10
start with 10
maxvalue 300
minvalue 5
初始值必须在maxvalue minvalue 之间
提取值大于范围时,会报错ora-08004
创建有最大值循环序列
create sequence seq_test2
increment by 10
start with 10
maxvalue 300
minvalue 5
cycle ;
初始值和第二值必须在maxvalue minvalue 之间
创建有缓存的循环队列
create sequence seq_test5
increment by 10
start with 10
maxvalue 500
minvalue 9
cycle
cache 50;
缓存设置的数必须小于每次循环周期的数。循环数的取值刚好在最大值和最小值之间也不行
修改序列
ALTER SEQUENCE 序列名称 MAXVALUE 5000 CYCLE;
注意:不能修改序列的 START WITH 参数
删除序列
DROP SEQUENCE 序列名称;
创建自己用户的同义词
create synonym OWNERS for T_OWNERS;
创建公共用户同义词
create public synonym OWNERS2 for T_OWNERS;
使用同义词
select * from OWNERS ;
索引
创建普通索引
create index index_owners_name on T_OWNERS(name)
测试索引效率
create table T_INDEXTEST (
ID NUMBER,
NAME VARCHAR2(30)
);
BEGIN
FOR i in 1..1000000
loop
INSERT INTO T_INDEXTEST VALUES(i,'AA'||i);
end loop;
commit;
END;
CREATE INDEX INDEX_TESTINDEX on T_INDEXTEST(name)
SELECT * from T_INDEXTEST where ID=765432;
SELECT * from T_INDEXTEST where NAME='AA765432';
创建唯一索引
create unique index index_owners_watermeter on
T_OWNERS(watermeter);
创建联合索引
create index owners_index_ah
on T_OWNERS(addressid,housenumber);
创建反向索引
create index 索引名称 on 表名(列名) reverse;
创建位图索引
create bitmap index index_owners_typeid
on T_OWNERS(ownertypeid)
PLSQL语法
PL/SQL(Procedure Language/SQL)是 Oracle 对 sql 语言的过程化扩展,指
在 SQL 命令语言中增加了过程处理语句(如分支、循环等),使 SQL 语言具有
过程处理能力。
基本语法
[declare
--声明变量
]
begin
--代码逻辑
[exception
--异常处理
]
end;
声明变量引用某表某列的字段类型%TYPE
declare
v_price number(10,2);--单价
v_usenum T_ACCOUNT.USENUM%TYPE;--水费字数
v_num0 T_ACCOUNT.NUM0%TYPE;--上月字数
v_num1 T_ACCOUNT.NUM1%TYPE;--本月字数
v_usenum2 number(10,2);--使用吨数
v_money number(10,2);--水费金额
begin
--对单价进行赋值
v_price:=3.45;
--v_usenum:=8090;
select usenum,num0,num1 into v_usenum,V_num0,V_num1 from
T_ACCOUNT
where year='2012' and month='01' and owneruuid=1;
--使用吨数
v_usenum2:= round(v_usenum/1000,2);
--计算金额
v_money:=v_price*v_usenum2;
DBMS_OUTPUT.put_line('单价:'||v_price||'吨数:'
||v_usenum2||'金额:'||v_money||'上月字数:'||v_num0||'本月
字数'||v_num1);
end;
声明变量引用某表某列的字段类型%ROWTYPE
--变量的用法--
declare
v_price number(10,2);--单价
v_account T_ACCOUNT%ROWTYPE;--记录型
v_usenum2 number(10,2);--使用吨数
v_money number(10,2);--水费金额
begin
--对单价进行赋值
v_price:=3.45;
--赋值
select * into v_account from T_ACCOUNT
where year='2012' and month='01' and owneruuid=1;
--使用吨数
v_usenum2:= round(v_account.usenum/1000,2);
--计算金额
v_money:=v_price*v_usenum2;
DBMS_OUTPUT.put_line('单价:'||v_price||'吨数:'
||v_usenum2||'金额:'||v_money||'上月字数:
'||v_account.num0||'本月字数'||v_account.num1);
end;
异常处理
ACCESS_INTO_NULL 未定义对象
CASE_NOT_FOUND CASE 中若未包含相应的 WHEN ,并且没有设置 ELSE 时
COLLECTION_IS_NULL 集合元素未初始化
CURSER_ALREADY_OPEN 游标已经打开
DUP_VAL_ON_INDEX 唯一索引对应的列上有重复的值
INVALID_CURSOR 在不合法的游标上进行操作
INVALID_NUMBER 内嵌的 SQL 语句不能将字符转换为数字
NO_DATA_FOUND 使用 select into 未返回行
TOO_MANY_ROWS 执行 select into 时,结果集超过一行
ZERO_DIVIDE 除数为 0
SUBSCRIPT_BEYOND_COUNT 元素下标超过嵌套表或 VARRAY 的最大值
SUBSCRIPT_OUTSIDE_LIMIT 使用嵌套表或 VARRAY 时,将下标指定为负数
VALUE_ERROR 赋值时,变量长度不足以容纳实际数据
LOGIN_DENIED PL/SQL 应用程序连接到 oracle 数据库时,提供了不正确的用户名或密码
NOT_LOGGED_ON PL/SQL 应用程序在没有连接 oralce 数据库的情况下访问数据
PROGRAM_ERROR PL/SQL 内部问题,可能需要重装数据字典& pl./SQL 系统包
ROWTYPE_MISMATCH 宿主游标变量与 PL/SQL 游标变量的返回类型不兼容
SELF_IS_NULL 使用对象类型时,在 null 对象上调用对象方法
STORAGE_ERROR 运行 PL/SQL 时,超出内存空间
SYS_INVALID_ID 无效的 ROWID 字符串
TIMEOUT_ON_RESOURCE Oracle 在等待资源时超时
--变量的用法--
declare
v_price number(10,2);--水费单价
v_usenum T_ACCOUNT.USENUM%type; --水费字数
v_usenum2 number(10,3);--吨数
v_money number(10,2);--金额
begin
v_price:=2.45;--水费单价
select usenum into v_usenum from T_ACCOUNT where
owneruuid=1 and year='2012' and month='01';
--字数换算为吨数
v_usenum2:= round( v_usenum/1000,3);
--计算金额
v_money:=round(v_price*v_usenum2,2);
dbms_output.put_line('单价:'||v_price||'吨
数:'||v_usenum2||'金额:'||v_money);
exception
when NO_DATA_FOUND then
dbms_output.put_line('未找到数据,请核实');
when TOO_MANY_ROWS then
dbms_output.put_line('查询条件有误,返回多条信息,请核实');
end;
条件判断
if 条件 then
业务逻辑
elsif 条件 then
业务逻辑
else
业务逻辑
end if;
declare
v_price1 number(10,2);--不足 5 吨的单价
v_price2 number(10,2);--超过 5 吨不足 10 吨单价
v_price3 number(10,2);--超过 10 吨单
v_account T_ACCOUNT%ROWTYPE;--记录型
v_usenum2 number(10,2);--使用吨数
v_money number(10,2);--水费金额
begin
--对单价进行赋值
v_price1:=2.45;
v_price2:=3.45;
v_price3:=4.45;
--赋值
select * into v_account from T_ACCOUNT
where year='2012' and month='01' and owneruuid=1;
--使用吨数
v_usenum2:= round(v_account.usenum/1000,2);
--计算金额(阶梯水费)
if v_usenum2<=5 then--第一个阶梯
v_money:=v_price1*v_usenum2;
elsif v_usenum2>5 and v_usenum2<=10 then --第二个阶梯
v_money:=v_price1*5 + v_price2*( v_usenum2-5);
else --第三个阶梯
v_money:=v_price1*5 +v_price2*5 + v_price3*( v_usenum2-10 );
end if;
DBMS_OUTPUT.put_line('吨数:' ||v_usenum2||'金额:'||v_money||'上月字数:'||v_account.num0||'本月字数'||v_account.num1);
exception
when NO_DATA_FOUND then
DBMS_OUTPUT.put_line('没有找到数据');
when TOO_MANY_ROWS then
DBMS_OUTPUT.put_line('返回的数据有多行');
end;
无条件循环loop
declare
v_num number:=1;
begin
loop
dbms_output.put_line(v_num);
v_num:=v_num+1;
exit when v_num>100;
end loop;
end ;
条件循环while loop
declare
v_num number:=1;
begin
while v_num<=100
loop
dbms_output.put_line(v_num);
v_num:=v_num+1;
end loop;
end ;
条件循环for loop
begin
for v_num in 1..100
loop
dbms_output.put_line(v_num);
end loop;
end;
游标
游标是系统为用户开设的一个数据缓冲区,存放 SQL 语句的执行结果。我们
可以把游标理解为 PL/SQL 中的结果集
声明游标
declare
v_pricetable T_PRICETABLE%rowtype;--价格行对象
cursor cur_pricetable is select * from T_PRICETABLE where
ownertypeid=1;--定义游标
begin
使用游标
open cur_pricetable;--打开游标
loop
fetch cur_pricetable into v_pricetable;--提取游标到变量
exit when cur_pricetable%notfound;--当游标到最后一行下面退
出循环
dbms_output.put_line( '价格:'
||v_pricetable.price ||'吨位:
'||v_pricetable.minnum||'-'||v_pricetable.maxnum );
end loop;
close cur_pricetable;--关闭游标
end ;
声明有参数游标
declare
v_pricetable T_PRICETABLE%rowtype;--价格行对象
cursor cur_pricetable(v_ownertypeid number) is select *
from T_PRICETABLE where ownertypeid=v_ownertypeid;--定义游
标
begin
使用有参数游标
open cur_pricetable(2);--打开游标
loop
fetch cur_pricetable into v_pricetable;--提取游标到变量
exit when cur_pricetable%notfound;--当游标到最后一行下面退
出循环
dbms_output.put_line('价格:'||v_pricetable.price ||'吨
位:'||v_pricetable.minnum||'-'||v_pricetable.maxnum );
end loop;
close cur_pricetable;--关闭游标
end ;
for 循环提取游标值
自动关闭游标
declare
cursor cur_pricetable(v_ownertypeid number) is select *
from T_PRICETABLE where ownertypeid=v_ownertypeid;--定义游
标
begin
for v_pricetable in cur_pricetable(3)
loop
dbms_output.put_line('价格:'||v_pricetable.price ||'吨
位:'||v_pricetable.minnum||'-'||v_pricetable.maxnum );
end loop;
end ;
存储函数
创建存储函数
create function fn_getaddress(v_id number)
return varchar2
is
v_name varchar2(30);
begin
select name into v_name from t_address where id=v_id;
return v_name;
end;
使用存储函数
select fn_getaddress(3) from dual
select id 编号,name 业主名称,fn_getaddress(addressid) 地址 from t_owners
创建存储过程
CREATE [ OR REPLACE ] PROCEDURE 存储过程名称
(参数名 类型, 参数名 类型, 参数名 类型)
IS|AS
变量声明部分;
BEGIN
逻辑部分
[EXCEPTION
异常处理部分]
END;
过程参数的三种模式:
IN 传入参数(默认)
OUT 传出参数 ,主要用于返回程序运行结果
IN OUT 传入传出参数
创建无参数存储过程
--增加业主信息序列
create sequence seq_owners start with 11;
--增加业主信息存储过程
create or replace procedure pro_owners_add
(
v_name varchar2,
v_addressid number,
v_housenumber varchar2,
v_watermeter varchar2,
v_type number
)
is
begin
insert into T_OWNERS
values( seq_owners.nextval,v_name,v_addressid,v_housenumb
er,v_watermeter,sysdate,v_type );
commit;
end;
使用无参数存储过程
call pro_owners_add('赵伟',1,'999-3','132-7',1);
创建带传出参数的存储过程
--增加业主信息存储过程
create or replace procedure pro_owners_add
(
v_name varchar2,
v_addressid number,
v_housenumber varchar2,
v_watermeter varchar2,
v_type number,
v_id out number
)
is
begin
select seq_owners.nextval into v_id from dual;
insert into T_OWNERS
values( v_id,v_name,v_addressid,v_housenumber,v_watermete
r,sysdate,v_type );
commit;
end;
调用带传出参数该存储过程
declare
v_id number;--定义传出参数的变量
begin
pro_owners_add('王旺旺',1,'922-3','133-7',1,v_id);
DBMS_OUTPUT.put_line('增加成功,ID:'||v_id);
end;
触发器
在表上执行数据操作语句(Insert,update,delete)在指定的表上执行存储过程
创建触发器
CREATE [or REPLACE] TRIGGER 触发器名
BEFORE | AFTER
[DELETE ][[or] INSERT] [[or]UPDATE [OF 列名]]
ON 表名
[FOR EACH ROW ][WHEN(条件) ]
declare
……
begin
PLSQL 块
End ;
FOR EACH ROW 作用是标注此触发器是行级触发器 语句级触发器
创建前置触发器
create or replace trigger tri_account_updatenum1
before
update of num1
on t_account
for each row
declare
begin
:new.usenum:=:new.num1-:new.num0;
end;
创建后置触发器
--创建业主名称修改日志表:用于记录业主更改前后的名称
create table t_owners_log
(
updatetime date,
ownerid number,
oldname varchar2(30),
newname varchar2(30)
);
--创建后置触发器,自动记录业主更改前后日志
create trigger tri_owners_log
after
update of name
on t_owners
for each row
declare
begin
insert into t_owners_log
values(sysdate,:old.id,:old.name,:new.name);
end;
使用触发器
--更新数据
update t_owners set name='杨小花' where id=3;
commit;
--查询日志表
select * from t_owners_log;
综合案例
编写 PL/SQL ,用水吨数 12 吨,业主类型为 1,计算阶梯水费。
5 吨以下的价格为 2.45
超过 5 吨不足 10 吨的价格为 3.45
超过 10 吨以上的价格为 4.45
金额=0
循环价格表{
if( 上限值为空 或者 总吨数<上限值 ) -- 最高阶梯
{
//此为最后阶梯 ,数量为超过上限值部分的吨数
金额=金额+ 价格*(总吨数- 上限值)
退出循环
}
else
{
//此为非最后阶梯 ,数量为区间内的吨数
金额=金额+ 价格*(上限值- 下限值)
}
}
declare
v_ownertypeid number;--业主类型 ID
v_usenum2 number(10,2);--总吨数
v_money number(10,2);--总金额
cursor cur_pricetable(v_type number) is select * from
t_pricetable where ownertypeid=v_type;--价格游标
v_pricetable t_pricetable%rowtype;--每阶梯价格对象
begin
v_ownertypeid:=1;
v_usenum2:=12;
v_money:=0;
for v_pricetable in cur_pricetable(v_ownertypeid)
loop
if v_pricetable.maxnum is null or
v_usenum2<=v_pricetable.maxnum then
--最后阶梯 (总吨数-下限值)*价格
v_money:=v_money+
v_pricetable.price*(v_usenum2-v_pricetable.minnum);
exit;
else
--非最后阶梯 (上限值-下限值)* 价格
v_money:=v_money+
v_pricetable.price*(v_pricetable.maxnum-v_pricetable.minn
um);
end if;
end loop;
DBMS_OUTPUT.put_line('阶梯水费金额:'||v_money);
end;
储函数综合案例:创建计算阶梯水费的函数,参数为业主类型、吨数。
create or replace function fn_calmoney(v_ownertypeid number,v_usenum2 number)
return number
is
v_pricetable t_pricetable%rowtype;--价格行对象
v_money number(10,2);--金额
cursor cur_pricetable(v_type number) is select * from
t_pricetable where ownertypeid=v_type order by minnum;--定
义游标
begin
v_money:=0;--金额
for v_pricetable in cur_pricetable(v_ownertypeid)
loop
--计算阶梯水费
--如果水费小于最大值,或最大值为 null 表示此阶梯为最后一个阶梯,
--价格*(总吨数-此阶梯下限值)
if v_usenum2<= v_pricetable.maxnum or
v_pricetable.maxnum is null then
v_money:=v_money+ v_pricetable.price* ( v_usenum2 -
v_pricetable.minnum);
exit;
else -- 价格*(此阶梯上限值-此阶梯下限值)
v_money:=v_money+ v_pricetable.price*
(v_pricetable.maxnum-v_pricetable.minnum );
end if;
end loop;
return v_money;
end;
测试此函数:
select fn_calmoney(1,12) from dual;
触发器综合案例:当用户输入本月累计数后,自动计算阶梯水费。
create or replace trigger tri_account_updatenum1
before
update of num1
on t_account
for each row
declare
v_usenum2 number(10,2);--吨数
begin
--使用数赋值
:new.usenum:=:new.num1-:new.num0;
v_usenum2:= round( :new.usenum/1000,3);--计算吨数
:new.money:=fn_calmoney(:new.ownertype,v_usenum2);--对金
额列赋值
end ;
存储过程综合案例。
需求:增加业主信息时,同时在账务表(account)增加一条记录,年份与月份
为当前日期的年月,初始值(num0)为 0,其它字段信息(区域)与 t_owners
表一致
难点分析:
- 如何取得年和月 用 to_char()函数
- 如何取得区域 ID 参数中没有直接提供区域 ID,我们可以通过 addressid
到 address 表查询
create or replace procedure pro_owners_add
(
v_name varchar2,
v_addressid number,
v_housenumber varchar2,
v_watermeter varchar2,
v_type number,
v_ownersuuid out number
)
is
v_area number;--区域编号
v_year char(4);--年份
v_month char(2);--月份
begin
--提取序列值到变量
select seq_owners.nextval into v_ownersuuid from dual;
--根据地址编号查询区域编号
select areaid into v_area from t_address where
id=v_addressid;
--年份
v_year:=to_char(sysdate ,'yyyy');
--月份
v_month:=to_char(sysdate,'mm');
--增加业主信息
insert into t_owners
values( v_ownersuuid,v_name,v_addressid,v_housenumber,v_w
atermeter,sysdate,v_type );
--增加账务表信息
insert into t_account
(id,owneruuid,ownertype,areaid,year,month,num0)
values
(seq_account.nextval,v_ownersuuid,v_type,v_area,v_year,
v_month,0 );
commit;
exception
when NO_DATA_FOUND then
v_ownersuuid:=-1;
rollback;
end;