【Oracle】常用命令汇总

本文基于黑马程序员文档做的二次总结,如有侵权,请联系本人删除。

文章目录

字段定义

创建表空间

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. 字符型
    (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 实现数据删除?

  1. delete 删除的数据可以 rollback
  2. delete 删除可能产生碎片,并且不释放空间
  3. 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

表一致

难点分析:

  1. 如何取得年和月 用 to_char()函数

  2. 如何取得区域 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;

相关推荐
Mephisto.java9 分钟前
【大数据学习 | kafka高级部分】kafka的kraft集群
大数据·sql·oracle·kafka·json·hbase
Mephisto.java11 分钟前
【大数据学习 | kafka高级部分】kafka的文件存储原理
大数据·sql·oracle·kafka·json
十叶知秋41 分钟前
【jmeter】jmeter的线程组功能的详细介绍
数据库·jmeter·性能测试
瓜牛_gn2 小时前
mysql特性
数据库·mysql
奶糖趣多多3 小时前
Redis知识点
数据库·redis·缓存
CoderIsArt4 小时前
Redis的三种模式:主从模式,哨兵与集群模式
数据库·redis·缓存
师太,答应老衲吧6 小时前
SQL实战训练之,力扣:2020. 无流量的帐户数(递归)
数据库·sql·leetcode
Channing Lewis7 小时前
salesforce case可以新建一个roll up 字段,统计出这个case下的email数量吗
数据库·salesforce
毕业设计制作和分享9 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
ketil279 小时前
Redis - String 字符串
数据库·redis·缓存