Oracle 常见知识汇总
常见操作系列
1.0 表空间和分配用户操作
/*第1:创建临时表空间 */
create temporary tablespace Mcally_temp tempfile 'D:\oracle\oradata\Oracle9i\Mcally_temp.dbf' size 50m autoextend on next 50m maxsize 20480m extent management local;
/*第2:创建数据表空间 */
create tablespace Mcally_data datafile 'D:\oracle\oradata\Oracle9i\Mcally_data.dbf' size 50m autoextend on next 50m maxsize 20480m extent management local;
/*第3:创建用户并指定表空间 */
create user Mcally identified by Mcally default tablespace Mcally_data temporary tablespace Mcally_temp;
/*或者这样给已经有的用户分配表空间*/
alter user Mcally default tablespace Mcally_data --为用户分配表空间
/*创建用户*/
create user 用户名 identified by 密码;
/*修改用户的密码*/
alter user 用户名 identified by 新密码
------扩展-----------------------------------------------------------------------------------------------
---授予某一项权限
grant create view to 用户名;
---撤销角色/权限
revoke 角色|权限 from 用户名;
---查看自身有哪些角色
select * from user_role_privs;
---查看自身的角色和权限
select * from role_sys_privs;
---修改用户处于锁定(非锁定)状态,锁定状态是不能登录的
alter user 用户名 account lock|unlock;
------扩展----------------------------------------------------------------------------------------------
/*第4:给用户授予权限 */
grant connect,resource,dba to username; --为角色授权
/*第5:查询用户 */
select * from all_users;
/*第6:用户登录 */
sqlplus 用户名/密码 [@host_string] [用户身份];
//普通用户
sqlplus 用户名/密码;
//系统管理员登录,必须指定身份,as sysdba或者as sysoper,可以不输入密码也能正常登陆
sqlplus sys/ as sysdba;
/*用户登录失败原因:
被锁定
没有登录权限
忘记密码
用户不存在*/
/*第7:切换用户:*/
connect 用户名/密码;
/*第8:查询表空间信息和表空间数据文件存放位置sql:*/
select * from dba_tablespaces;
select * from dba_data_files;
1.1 常用命令
---创建---
create table test(
name varchar2(20)
);
create table test1 as select * from test;
---查询---
select * from test;
---插入---
insert into test(name) value('Mcally');
---更新---
update test set name='king' where name='Mcally';
---删除---
delete from test where name='Mcally';
truncate table test ---删除表数据,保留表结构,数据无法恢复。
desc 表名
---闪回删除————10g新特性
drop table 表名
---查看回收站
show recyclebin;
---从回收站恢复表
flashback table 表名 to before drop;
---清空回收站
purge recyclebin;
create sequence student_id_seq; --创建序列
insert into student values (student_id_seq.nextval,'张三'); ---取序列值
insert into student values (student_id_seq.nextval,'李四');
----Oracle 多表更新方法:
update test1 set (test1.name,test1.age)=
(select test2.name,test2.age from test2 where test2.id=test1.id);
----通用的方法:
update test1
set name=(select name from test2 where test2.id=test1.id),
age=(select age from test2 where test2.id=test1.id);
1.2 函数
---去重---
select distinct 列名 from 表名;
----左外连接 left join 两表匹配的行 + 左表中剩余的行
select a.sno,sname,cno from student a left outer join score c on a.sno=c.sno
--等价于
select a.sno,sname,cno from student a,score c where a.sno=c.sno(+)
-----右外连接 right join 两表匹配的行 + 右表中剩余的行
select a.sno,sname,cno from student a right outer join score c on a.sno=c.sno
--等价于
select a.sno,sname,cno from student a,score c where a.sno(+)=c.sno
-----全连接 full join
select a.sno,sname,cno from student a full outer join score c on a.sno=c.sno
----排序 order by
select * from 表名 order by 列名 ASC|DESC;
select * from 表名 order by 列名1 ASC, 列名2 DESC,列名3 DESC;
----分组 group by
select gender,count(gender) from A group by gender
----聚合比较 having
----聚合函数作比较要放在having中,而不能放在where中。分组->计算聚合->聚合比较
select job,avg(sal) from A where gender='man' group by job having avg(sal)>1500
-------集合运算
SELECT column_name(s) FROM table_name1
UNION
SELECT column_name(s) FROM table_name2
//可重复
SELECT column_name(s) FROM table_name1
UNION ALL
SELECT column_name(s) FROM table_name2
-----求交集,两表相同的部分
SELECT column_name(s) FROM table_name1
intersect
SELECT column_name(s) FROM table_name2
-----求差集,两表不相同的部分
SELECT column_name(s) FROM table_name1
minus
SELECT column_name(s) FROM table_name2
--------case when then else end
select (case whe sex=0 then '男' when sex='1' then '女' else '不知性别' end) sexstr from test
---------decode-----
select decode(sex,0,'男',1,'女','不知性别') sexstr from test
----------------------------------------------------单行函数----------------------------------------------------
1.字符函数
//将第一个字母转成大写
initcap(n)
//从m位置开始在x中查找字符串y出现的位置,n是出现次数
instr(x,y,m,n)
//求字符串长度
length(n)
//将字符串各字符转换成小写
lower(x)
//大写......
upper(x)
//在字符串x左边补齐字符y(缺省则补空格),得到总长为n的字符串
lpad(x,n,y)
//在字符串x右边......
rpad(x,n,y)
//去掉左边\右边\两边去掉指定字符
select trim(leading '*' from '**sbash**') from dual;
//x左边去掉指定字符y,默认去空格
ltrim(x,y)
//x右边......
rtrim(x,y)
//如果x不是null,则返回x,否则返回y
nul(x,y)
//如果x不是null,则返回y,否则返回z
nul2(x,y,z)
//如果x是数字,则返回x,否则返回y
nanvl(x,y)
//x中替换y为z
replace(x,y,z)
//从字符串x中的m开始取长度为n的子串,n缺省时取到结尾
substr(x,m,n)
//连接字符串
concat(m,n)
2. 数字函数
round:四舍五入
round(5.89,-1) // 10 保留十位数
round(5.89) // 6 保留个位数
round(5.89,1) // 5.9
round(15.89,-1) // 20
round(15.89,-2) // 0
trunc:截断
trunc(5.89,-1) // 0
trunc(5.89) // 5
trunc(5.89,1) // 5.8
3. 转换函数
//转换为日期
select to_date('2018-06-18','yyyy-MM-dd') from dual
//转为为二进制数
bin_to_num(n)
//2018-06-18 09:35:45
select to_char(sysdate,'yyyy-MM-dd HH24:mi:ss') from dual
4. 日期函数
//获取系统时间
select sysdate from dual //18-6月 -18
//求下个月最后一天
select last_day(add_mon ths(sysdate,1)) from dual
//从x开始,下一个第n天的日期(从星期天开始算)
select next_day(sysdate,n) from dual
//-.03225806
select months_between(sysdate,sysdate+1) from dual
//18-6月 -18 01-1月 -18 01-7月 -18
select round(sysdate),round(sysdate,'yyyy'),round(sysdate,'MM') from dual
//18-6月 -18 01-1月 -18 01-6月 -18
select trunc(sysdate),trunc(sysdate,'yyyy'),trunc(sysdate,'MM') from dual
//2018-06-18 09:35:45
select to_char(sysdate,'yyyy-MM-dd HH24:mi:ss') from dual
----------------------------------------------------聚合函数----------------------------------------------------
//count,返回非空数据的条数
select count(*) from A
//min、max求最值
select min(列名),max(列名) from A
//sum求和,avg求均值
select sum(列名),avg(列名) from A
select wm_concat(name) from user
-------------------------------------------------单行子查询-------------------------------------------------
//where中————查询年龄 小于 平均年龄的学生的学号和姓名
select id,name from student where age< (select avg(age) from student);
//语法错误,聚合函数应该在having中做比较
select id,name from student where age<avg(age);
//having中————查询课程为‘c002’的平均成绩 大于 所有学生的平均分
select cno,avg(score) from sc where cno='c002' group by cno
having avg(score)>(select avg(score) from sc);
//from中————查询哪些员工的工资 高于 所任职位的平均工资
select ename,sal,avgjob from emp a,
(select avg(sal),job from emp group by job) b
where a.job=b.job and a.sal>b.avgjob;
//错误写法,子查询返回的是一个表
select ename,工资 from 表名 where 工资>(select 职位,avg(工资) from 表名 group by 职位);
------------------------------------------------------多行子查询--------------------------------------
---<1>使用IN操作符
//查询 各个职位中工资最高 的员工信息 (两个职位的最高工资相同时,只显示其中一个)
select empno,ename,job,sal from emp where sal in(select max(sal) from emp group by job)
//上面的改进版,由于查询多了个job,所以就算sal相同也会显示
select empno,ename,job,sal from emp where (sal,job) in(select max(sal),job from emp group by job)
---<2>使用exists操作符 是否存在,返回真或者假。
select empno,ename,sal from emp where exists(select * from dept where deptno = '40')
----------------通配符 % _---------------------------
/*
% :0个或多个字符;
_ :1个字符
[charlist]: 字符列中的任何单一字符
[^charlist]或者[!charlist] :不在字符列中的任何单一字符
*/
select * from 表名 where 列名 like 's_a%y';
select * from 表名 where 列名 not like 's_a%y';
---以 "A" 或 "L" 或 "N" 开头的人:
SELECT * FROM Persons WHERE City LIKE '[ALN]%'
SELECT * FROM Persons WHERE City LIKE '[!ALN]%'
---转义escape,可以将一个字符当成是转义字符
select * from 表名 where 列名 like 's\_a\%y' escape '\';
select * from 表名 where 列名 like 's$_a$%y' escape '$';
----------------通配符 % _---------------------------
1.3 约束
---创建表时添加约束:
create table student
(
id number(5) primary key, ---主键约束
team_id varchar2(5) foreign key references team(id), ----外键约束
address varchar2(20) not null, -----非空约束
country varchar2(10) unique, -----唯一约束
gender varchar2(2) check(gender='man' or gender='woman') ----检查约束
);
//或
create table student
(
id number(5),
team_id varchar2(5),
country varchar2(10),
gender varchar2(2),
constraint pk_student_id primary key(id),
constraint fk_student_tid foreign key(team_id) references team(id),
constraint uk_student_co unique(country),
constraint student_sex_ck check(gender='man' or gender='woman')
);
---创建表后添加约束:
alter table A add primary key(id);
alter table A add foreign key(team_id) references team(id);
alter table A modify address not null;
alter table A add unique (country);
alter table A add check(gender='man' or gender='woman');
--或
alter table A add constraint pk_student_id PRIMARY KEY (id);
alter table A add constraint fk_student_tid foreign key(team_id) references team(id);
alter table A add constraint uk_student_co unique (country);
alter table A add constraint student_sex_ck check(gender='man' or gender='woman');
---删除约束:
alter table A drop constraint 约束名;
---查看约束:
desc user_constraints;
select owner,constraints_name,constraints_type,table_name from user_constraints;
desc user_cons_columns;
select owner,constraints_name,table_name,column_name from user_cons_columns;
1.4 视图
---创建---
create or replace noforce view v_emp ("empno","ename","sal")
as select empno,ename,sal from emp where sal>1000;
--or--
create or replace noforce view v_emp
as select empno,ename,sal from emp;
---对视图的DML操作只能对简单的视图(基于一张表)进行DML操作。
insert into v_emp(id,name) values(001,'Jason')
insert into v_emp values(001,'Jason','lloijs',2461)
--<1> with check option
create or replace noforce view v_emp
as select empno,ename,sal from emp where sal>1000 with check option;
---插入失败,要满足where后面的条件
insert into v_emp values(001,'Jason',500)
---<2> with read only
create or replace noforce view v_emp
as select empno,ename,sal from emp with read only;
//插入失败,因为是只读with read only
insert into v_emp values(001,'Jason',500)
1.5 索引
CREATE INDEX
CREATE [unique] INDEX [user.]index
ON [user.]table (column [ASC | DESC] [,column
[ASC | DESC] ] ... )
[CLUSTER [scheam.]cluster]
[INITRANS n]
[MAXTRANS n]
[PCTFREE n]
[STORAGE storage]
[TABLESPACE tablespace]
[NO SORT]
/*
其中:
schema ORACLE模式,缺省即为当前帐户
index 索引名
table 创建索引的基表名
column 基表中的列名,一个索引最多有16列,long列、long raw 列不能建索引列
DESC、ASC 缺省为ASC即升序排序
CLUSTER 指定一个聚簇(Hash cluster不能建索引)
INITRANS、MAXTRANS 指定初始和最大事务入口数
Tablespace 表空间名
STORAGE 存储参数,同create table 中的storage.
PCTFREE 索引数据块空闲空间的百分比(不能指定pctused)
NOSORT 不(能)排序(存储时就已按升序,所以指出不再排序)
*/
----创建索引
create index 索引名 on 表名(列名);
----创建组合索引
create index 索引名 on 表名(列名1,,列名2);
create index index_name on table_name(field_name)
tablespace tablespace_name
pctfree 5
initrans 2
maxtrans 255
storage
(
minextents 1
maxextents 16382
pctincrease 0
);
------创建基于函数的索引常用与UPPER、LOWER、TO_CHAR(date)等函数分类上
create index idx_func on emp(UPPER(ename)) tablespace tablespace_name;
------创建位图索引对基数较小,且基数相对稳定的列建立索引时,首先应该考虑位图索引
create bitmap index idx_bitm on class (classno) tablespace tablespace_name;
----明确地创建唯一索引 可以用create unique index语句来创建唯一索引
create unique index dept_unique_idx on dept(dept_no) tablespace idx_1;
-----创建与约束相关的索引可以用using index字句,为与unique和primary key约束相关的索引
alter table table_name add constraint PK_primary_keyname primary key(field_name)
using index tablespace tablespace_name;
-----创建反向键索引:
CREATE INDEX 索引名 ON 表名 (列名) reverse
TABLESPACE 表空间名;
----提高索引查询效率
alter index idx_name rebuild nologging;
----对于分区索引
alter index idx_name rebuild partition partition_name nologging;
------如何创建局部区索引?
create index TG_CDR04_SERV_ID_IDX on TG_CDR04(SERV_ID)
Pctfree 5
Tablespace TBS_AK01_IDX
Storage(
MaxExtents 32768
PctIncrease 0
FreeLists 1
FreeList Groups 1
)
local
/
----删除索引
drop index 索引名;
----查询索引
--根据索引名,查询表索引字段
select * from user_ind_columns where index_name='索引名';
--根据表名,查询一张表的索引
select * from user_indexes where table_name='表名';
1.6 临时表
-----什么是临时表,用户做一个操作查询出几百几千条数据,我们可以把数据放在内存中。当有很多用户都这样做,内存空间不足,这个时候就需------要把数据保存在磁盘上。对于 oracle 就提供了一种临时表用于存放这些数据。
-----1.事务级 (只要会话不断开,临时表一直有效,会话断开后,临时表内容清空)
-----2.会话级 (只在一个事务内有效,事务提交后,临时表内容清空。)
on commit preserve rows --session 临时表
on commit delete rows --transaction 临时表
---创建sessione 级别的临时表只要用户不退出,临时表一直有效
create global temporary table session_test on commit preserve rows as select * from test;
---创建事务级别的临时表 这个临时表的作用范围只在一个事务内有效
create global temporary table transaction_test on commit delete rows as select * from test;
1.7扩展
--------------------------------第一种Oracle误删除数据恢复-------------------------------
select * from 表名 as of timestamp to_timestamp('2018-05-04 13:30:00','yyyy-MM-dd hh24:mi:ss')
select * from tableName AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '100' MINUTE)
-----查询这个时间点的数据
-----有可能因为系统时间和数据库时间不一致查不出数据 所以先查询数据库的时间
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual
-----语句就可以将恢复出来的数据插入原表中(注意要保证主键不重复)。
insert into 表名 (select * from 表名 as of timestamp to_timestamp('删除时间点','yyyy-mm-dd hh24:mi:ss'));
-----或者按照这个时间为准,找到数据删除时间之前的数据)
-----找到数据可以导出为Excel ,怎么恢复就不是问题了
flashback table 表名 to timestamp to_timestamp('2018-05-04 13:30:00','yyyy-MM-dd hh24:mi:ss')
----执行这条语句把数据恢复到这个时间点
-----报错: ORA-08189: 因为未启用行移动功能, 不能闪回表
alter table 表名 enable row movement ---开启行移动功能
-----------------------------------第二种Oracle误删除数据恢复--------------------------
----第二种数据恢复方法的原理是因为oracle数据库在删除表时会将删除信息存放于某虚拟“回收站”中而非直接清空,再此种状态下数据库标记该-----表的数据库为“可以复写”,所以在该块未被重新使用前依然可以恢复数据。该方法多用于drop删除。
----首先需要查询user_table视图找到被删除的表:
select table_name,dropped from user_tables
select object_name,original_name,type,droptime from user_recyclebin
----注意此时的表名称已经被重新命名,table_name和object_name就是回收站中的存放表名,如果管理员此时可以明确原表的名称可以通过
flashback table 原表名 to before drop
----语句进行数据恢复,如果不知道原表名称可以直接按照回收站中的表名称将数据恢复回复来,然后通过
flashback table "回收站中的表名(如:Bin$DSbdfd4rdfdfdfegdfsf==$0)" to before drop rename to 新表名
---语句就可以重新命名。
-----------------------------------第三种Oracle误删除数据恢复--------------------------
----第三种方法同样利用oracle数据库的闪回功能可以将数据库恢复到过去某一状态,语法如下:
alter database flashback on
flashback database to scn SCNNO;
flashback database to timestamp to_timestamp('frombyte 2007-2-12 12:00:00','yyyy-mm-dd hh24:mi:ss');
---介绍到这里有朋友发现问题了,oracle数据库提供了可以恢复数据的保障机制,但也不可避免的占用了大量空间,使用drop一个表或者delete----数据后空间并不能自动进行回收,如果确定需要删除的数据又不想无谓的占用空间该如何操作呢?我们可以使用以下两种方式:
---1、采用truncate方式进行截断。(但不能进行数据回恢复了)
---2、在drop时加上purge选项:drop table 表名 purge
---该选项也可以通过删除recyclebin区域来永久性删除表 ,原始删除表drop table emp cascade constraints
purge table emp;
----删除当前用户的回收站:
purge recyclebin;
----删除全体用户在回收站的数据:
purge dba_recyclebin
-----------------------------------------------------【数据导入】---------------------------------------------------
---1. 将D:\daochu.dmp 中的数据导入 orcl数据库中。
imp system/manager@orcl file=d:\daochu.dmp
---注:如果有的表已经存在,会提示报错,这个时候,在后面加上ignore=y,即已存在的表不导入
----2. 将d:\daochu.dmp中的表table1 导入
imp system/manager@orcl file=d:\daochu.dmp tables=(table1)
---注:导入导出时,有时候会出现权限不足的情况
---这时,需要登录plus进行授权,首先登录管理员账号,然后:
GRANT CREATE USER,DROP USER,ALTER USER ,CREATE ANY VIEW ,
DROP ANY VIEW,EXP_FULL_DATABASE,IMP_FULL_DATABASE,
DBA,CONNECT,RESOURCE,CREATE SESSION TO 用户名字
-----------------------------------------------------【数据导出】-------------------------------------------------
------1. 导出orcl中所有的表空间
exp system/manager@orcl file=d:\daochu.dmp full=y
------2. 导出system数据库中指定的表空间
exp system/manager@orcl file=d:\daochu.dmp owner=(system,sys)
-------3. 导出数据库中制定的表数据
exp system/manager@orcl file=d:\daochu.dmp tables=(table1,table2)
-------4. 导出数据库中table1中字段field1以“00”开头的数据
exp system/manager@orcl file=d:\daochu.dmp tables=(table1)query=\" where filed1 like '00%'\"
----注:如果要压缩,则在最后面加上compress=y即可
PL/Sql 学习
简介
PL/SQL是 Procedure Language & Structured Query Language 的缩写。ORACLE的SQL是支持ANSI(American national Standards Institute)和ISO92 (International Standards Organization)标准的产品。PL/SQL是对SQL语言存储过程语言的扩展。从ORACLE6以后,ORACLE的RDBMS附带了PL/SQL。它现在已经成为一种过程处理语言,简称PL/SQL。目前的PL/SQL包括两部分,一部分是数据库引擎部分;另一部分是可嵌入到许多产品(如C语言,JAVA语言等)工具中的独立引擎。可以将这两部分称为:数据库PL/SQL和工具PL/SQL。两者的编程非常相似。都具有编程结构、语法和逻辑机制。工具PL/SQL另外还增加了用于支持工具(如ORACLE Forms)的句法,如:在窗体上设置按钮等。本章主要介绍数据库PL/SQL内容。
1.0 PL/Sql块
PL/SQL程序由三个块组成,即声明部分、执行部分、异常处理部分。
DECLARE
--声明部分: 在此声明PL/SQL用到的变量,类型及游标,以及局部的存储过程和函数
--- 变量: a number(9) := 10;
--- 常量: PI CONSTANT number(9) := 3.141592653;
--- 赋值: := 或者 default 或者 into
BEGIN
-- 执行部分: 过程及SQL 语句 , 即程序的主要部分
EXCEPTION
-- 执行异常部分: 错误处理
END;
---其中:执行部分不能省略。
---PL/SQL块可以分为以下几类:
---1. 无名块或匿名块(anonymous):动态构造,只能执行一次,可调用其它程序,但不能被其它程序调用。
---2. 命名块(named):是带有名称的匿名块,这个名称就是标签。
---3. 子程序(subprogram):存储在数据库中的存储过程、函数等。当在数据库上建立好后可以在其它程序中调用它们。
---4. 触发器(Trigger):当数据库发生操作时,会触发一些事件,从而自动执行相应的程序。
---5. 程序包/包(package):存储在数据库中的一组子程序、变量定义。在包中的子程序可以被其它程序包或子程序调用。但如果声明的是局--------部子程序,则只能在定义该局部子程序的块中调用该局部子程序。
------------------------------------------------第一个pl/sql hello word--------------------------
----------就是这么简单----------
declare
begin
dbms_output.put_line('hello word');
end;
1.1 变量类型
---<1>标量类型变量<<重点>>
--- into 关键词可以将select得到的数据赋值给变量
sum_number number(5);
select count(*) into sum_number from student
--- %type 将表中的列的类型作为变量id的数据类型。
---id 表名.列名%type;
id test.id%type
select sno into id from student where sname='mike'
--- %rowtype 将表一行的类型作为变量id的数据类型。
---id 表名%rowtype;
id teset.id%rowtype
select * into id from student where sno='001'
---<2>复合类型变量
---允许用户在代码中使用“表”,以便存储多行数据。
set serveroutput on
declare
type t_table is table of varchar2(20) index by binary_integer;
t t_table;
begin
t(1) := 'a';
t(2) := 'b';
dbms_output.put_line(t.count())
end
1.2 条件判断循环语句
------------------------------------IF语句分支-------------------------------------------
IF condition1 THEN
statement1;
ELSIF condition2 THEN
statement12;
ELSE
statement3;
END IF;
------------------------------------CASE语句分支判定-----------------------------------------
CASE 变量
WHEN 值1 THEN statement1;
WHEN 值2 THEN statement2;
...
[ELSE statementS;]
END CASE;
-------------------------------------循环语句-----------------------------------------
----第一种
loop
statements;
end loop;
----第二种
while condition loop
statements;
end loop;
----第三种
for i in 1..10 loop
statements;
end loop;
----跳出循环
[exit when condition;]
statements;
1.3 is table of
指定是一个集合的表的数组类型,简单的来说就是一个可以存储一列多行的数据类型。
---IS TABLE OF :指定是一个集合的表的数组类型,简单的来说就是一个可以存储一列多行的数据类型。
---INDEX BY BINARY_INTEGER:指索引组织类型
---BULK COLLECT :指是一个成批聚合类型,简单的来说 , 它可以存储一个多行多列存储类型,采用BULK COLLECT可以将查询结果一次性地加-----载到集合中。
-----实例1
declare
type type_ename is table of emp.ename%type;
type type_job is table of emp.job%type;
type type_sal is table of emp.sal%type;
var_ename type_ename:=type_ename();
var_job type_job:=type_job();
var_sal type_sal:=type_sal();
begin
select ename,job,sal
bulk collect into var_ename,var_job,var_sal
from emp;
--输出雇员信息
for v_index in var_ename.first .. var_ename.last loop
dbms_output.put_line('雇员名称:'||var_ename(v_index)||' 职务:'||var_job(v_index)||' 工资:'||var_sal(v_index));
end loop;
end;
-----实例2
declare
type emp_table_type is table of emp%rowtype index by binary_integer;
var_emp_table emp_table_type;
begin
select *
bulk collect into var_emp_table
from emp;
/*输出雇员信息*/
for i in 1..var_emp_table.COUNT loop
dbms_output.put_line('雇员名称:'||var_emp_table(i).ename||' 职务:'||var_emp_table(i).job||' 工资:'||var_emp_table(i).sal);
end loop;
end;
1.4 RECORD
定义记录数据类型。它类似于C语言中的结构数据类型(STRUCTURE),PL/SQL提供了将几个相关的、分离的、基本数据类型的变量组成一个整体的方法,即RECORD复合数据类型。在使用记录数据类型变量时,需要在声明部分先定义记录的组成、记录的变量,然后在执行部分引用该记录变量本身或其中的成员。定义记录数据类型的语法如下:
TYPE RECORD_NAME IS RECORD(
V1 DATA_TYPE1 [NOT NULL][:=DEFAULT_VALUE],
V2 DATA_TYPE2 [NOT NULL][:=DEFAULT_VALUE],
VN DATA_TYPEN [NOT NULL][:=DEFAULT_VALUE]);
-- RECORD的使用举例
declare
type type_org_record is record(
v_name sf_org.org_name%type,
v_parent sf_org.parent_id%type);
v_record type_org_record;
begin
select org_name, parent_id into v_record from sf_org so
where so.org_id = &org_id;
dbms_output.put_line('部门名称:' || v_record.v_name);
dbms_output.put_line('上级部门编码:' || to_char(v_record.v_parent));
end;
1.5 VARRAY
数组是具有相同数据类型的一组成员的集合。每个成员都有一个唯一的下标,它取决于成员在数组中的位置。在PL/SQL中,数组数据类型是VARRAY(variable array,即可变数组)。
定义VARRAY数据类型的语法如下:
TYPE VARRAY_NAMEIS VARRAY(SIZE) OF ELEMENT_TYPE [NOT NULL];
其中,varray_name是VARRAY数据类型的名称,size是正整数,表示可以容纳的成员的最大数量,每个成员的数据类型是element_typeo默认时,成员可以取空值,否则需要使用NOT NULL加以限制。
-- VARRAY的使用举例
--- 注意,在引用数组中的成员时.需要在一对括号中使用顺序下标,下标从1开始而不是从0开始。
declare
type org_varray_type is varray(5) of varchar2(25);
v_arr_set org_varray_type;
begin
v_arr_set := org_varray_type('1','2','3','4','5');
dbms_output.put_line('输出1:' || v_arr_set(1) || '、'|| v_arr_set(2) || '、'|| v_arr_set(3) || '、'|| v_arr_set(4));
dbms_output.put_line('输出2:' || v_arr_set(5));
v_arr_set(5) := '5001';
dbms_output.put_line('输出3:' || v_arr_set(5));
end;
1.6 TABLE
---<1>存储单列多行
-- 这个和VARRAY类似。但是赋值方式稍微有点不同,不能使用同名的构造函数进行赋值。具体的如下:
declare
type org_table_type is table of varchar2(25)
index by binary_integer;
v_org_table org_table_type;
begin
v_org_table(1) := '1';
v_org_table(2) := '2';
v_org_table(3) := '3';
v_org_table(4) := '4';
v_org_table(5) := '5';
dbms_output.put_line('输出1:' || v_org_table(1) || '、'|| v_org_table(2) || '、'|| v_org_table(3) || '、'|| v_org_table(4)||'、'|| v_org_table(5));
end;
---<2> 存储多列多行和ROWTYPE结合使用
---采用bulkcollect可以将查询结果一次性地加载到collections中。而不是通过cursor一条一条地处理。
-- 存储多列多行和rowtype结合使用
declare
type t_type is table of sf_org%rowtype;
v_type t_type;
begin
select org_id, org_name, parent_id bulk collect into v_type from sf_org where sf_org.org_id <= 3;
for v_index in v_type.first .. v_type.last loop
dbms_output.put_line(v_type(v_index).org_id ||' '|| v_type(v_index).org_name ||' '|| v_type(v_index).parent_id );
end loop;
end;
---<3>存储多列多行和RECORD结合使用
---采用bulkcollect可以将查询结果一次性地加载到collections中。而不是通过cursor一条一条地处理。
-- 存储多列多行和RECORD结合使用
declare
type test_emp is record
(
c1 sf_org.org_name%type,
c2 sf_org.parent_id%type
);
type t_type is table of test_emp;
v_type t_type;
begin
select org_name, parent_id bulk collect into v_type from sf_org where sf_org.org_id <= 3;
for v_index in v_type.first .. v_type.last loop
dbms_output.put_line(v_type(v_index).c1 || ' ' || v_type(v_index).c2);
end loop;
end;
1.7 练习一把
------创建一个学生表-----
create table student (id integer,name varchar2(20));
------插入部分数据------
declare
i student.id%type;
name student.name%type;
num integer;
begin
for n in 1..10 loop
insert into student values(n,'user'||n);
end loop;
end;
declare
--定义一个自定义类型
type s_record is record(
id student.id%type,
name student.name%type
);
--定义一个存放自定义类型的集合(默认下标 index 为 number)
type s_recordtable is table of s_record index by binary_integer;
t s_recordtable;
m int :=1;
begin
for n in (select id,name from student)loop
t(m) := n;
m:=m+1;
end loop;
dbms_output.put_line('记录表的长度:'|| t.count());
for n in t.first()..t.last() loop
dbms_output.put_line(t(n).id ||'————'||t(n).name);
end loop;
end;
1.8 游标
---游标机制处理多行记录结果集;类似于指针,使程序一次可以处理其中的一行记录。
--<1>隐式游标
----执行一个SQL语句时,oracle服务器将自动创建一个隐式游标,这个游标存储执行SQL语句的结果。
----%found:布尔型,至少影响一行操作
----%notfound:布尔型,一行都不影响
----%isopen:布尔型,游标是否已打开
----%rowcount:number型,受SQL语句影响的行数
declare
a student%rowtype;
begin
select * into a from student where id='5';
update student set name='gaide' where id>'2';
dbms_output.put_line('受上面一句SQL语句影响的行数:'||sql%rowcount);
if sql%found then
dbms_output.put_line('找到数据:'||a.id);
end if;
exception when others then
dbms_output.put_line('没有找到数据!');
end;
--<2>显示游标
-----当查询结果返回多于一行时,必须使用显式游标。
-----声明->打开->检索->关闭
declare
cursor mycur(m integer) is select id,name from student where id>m; -- 定义游标
type s_record is record(
id integer,
name varchar2(20)
);
s s_record;
begin
open mycur(3); -- 打开游标
loop
fetch mycur into s; -- 利用游标检索数据,每次找出一行,然后赋值给s
exit when mycur%notfound; -- 游标到最后了
dbms_output.put_line(s.id||' '||s.name);
end loop;
close mycur; --关闭游标
end;
----下面的是另外一种查询返回多行结果的方法,t是记录表类型
for n in (select id,name from student)loop
t(m) := n;
m:=m+1;
end loop;
1.9 例外
和java异常一样,处理机制为向上处理机制语法和java差不多,只是没有finally
例外分类
(1)系统定义异常
(2)自定义例外
--------------------------------------------------常见系统定义异常--------------------------------------
---not data_found(没有找到数据)
---too_many_rows(select...into语句匹配多个行)
---zero_divide(被零除)
---value_error(算术或转换错误)
---timeout_on_resource(在等待资源时发生超时):发生的典型场景就是分布式数据库
---例子:被0除
declare
pnum number;
begin
pnum:=1/0;
exception
when zero_divide then dbms_output.put_line('0不能做除数');
when value_error then dbms_output.put_line('算术或转换例外');
when others then dbms_output.put_line('产生了其他例外');
end;
--------------------------------------------------自定义例外-------------------------------------------
---查询50号部门员工(不存在50号部门,抛异常)
declare
cursor c1 is select ename from emp where deptno=50;
pename emp.ename%type;
no_emp_deptno exception;----自定义异常
begin
open c1;
loop
fetch c1 into pename;
if c1%notfound then raise no_emp_deptno;----抛出异常
end if;
end loop;
close c1;
exception
when no_emp_deptno then dbms_output.put_line('没有该部门');
when others then dbms_output.put_line('跑出其他异常');
end;
存储过程
简介
存储过程,百度百科上是这样解释的,存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来调用存储过程。简单的说就是专门干一件事一段sql语句。可以由数据库自己去调用,也可以由java程序去调用。在oracle数据库中存储过程是procedure。
1.0 存储过程结构
Oracle存储过程包含三部分:过程声明,执行过程部分,存储过程异常(可写可不写,要增强脚本的容错性和调试的方便性那就写上异常处理)
基本语法如下:
CREATE OR REPLACE PROCEDURE SP_NAME( PM_NAME [IN/OUT/IN OUT] PM_TYPE…)
{AS}
[说明(变量定义)部分]
BEGIN
可执行部分
[EXCEPTION]
错误处理部分
END [SP_NAME];参数部分用于定义若干个参数(若没有参数,可以忽略)。参数有三种形式: IN / OUT 和 IN OUT, 默认为IN。
关键字AS 也可以写成 IS.
-----------------------------------------无参存储过程---------------------------------------
-- 写一个存储过程:实现向student表中插入10条记录的功能
create or replace procedure pro_student
as
--声明变量
begin
-- 执行逻辑代码
for i in 950..980 loop
insert into student(id,name) values(i,'asasas');
commit;
end loop;
--异常处理
end;
-----------------------------------------带参数的存储过程并且进行赋值1-----------------------------
create or replace procedure pro_student2(
p_id in number,-- 默认就是in 可以省略
p_name in varchar2, --没有被out修饰的参数是常量,不能被改变
p_result out student%rowtype-- 接收返回信息一行
)
as
begin
select * into p_result from student where id=p_id and name=p_name;
end;
-----------调用-------------------
declare
v_id student.id%type := '&请输入id';
v_name student.name%type :='&请输入姓名';
v_student student%rowtype;
begin
pro_student2(v_id,v_name,v_student);
dbms_output.put_line(v_student.id);
dbms_output.put_line(v_student.name);
dbms_output.put_line(v_student.sex);
dbms_output.put_line(v_student.birth);
dbms_output.put_line(v_student.address);
end;
-----------------------------------------带参数的存储过程并且进行赋值2-----------------------------
create or replace procedure pro_student3(
v_name in student.name%type,
v_sex in student.sex%type,
mycursor out sys_refcursor-----游标类型
)
as
v_sql varchar2(100);
begin
v_sql:='select * from student where 1=1';
if v_name is not null then
v_sql := v_sql || 'name like ''%'||v_name||'%''';
end if;
if v_sex is not null then
v_sql := v_sql || 'sex = '''||v_sex||'''';
end if;
dbms_output.put_line(v_sql);
open mycursor for v_sql;
close mycursor;
end;
-----------调用-------------------
declare
v_cursor sys_refcursor;
v_name student.name%type:='&请输入姓名';
v_sex student.sex%type:='&请输入性别';
v_student student%rowtype;
begin
pro_student3(v_name,v_sex,v_cursor);
loop
fetch v_cursor into v_student;
exit when v_cursor%notfound;
dbms_output.put_line(v_student.id||v_student.name);
end loop;
close v_cursor;
end;
1.1 运算符
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qYsuzeAD-1614843297642)(C:\Users\Mcally\AppData\Roaming\Typora\typora-user-images\image-20210301171143808.png)]
1.2 方法(函数)
基本语法:
CREATE [OR REPLACE] FUNCTION
[(param1,param2)]
RETURN IS|AS
[local declarations]
BEGIN
Executable Statements;
RETURN result;
EXCEPTION
Exception handlers;
END;方法和过程的区别:
1.DML相关的操作我们一般都使用存储过程实现
2.特定的公共的功能我们用方法实现
3.方法有显示的返回结果
4.方法中同样的也有 in out 关键字
----写一个方法实现根据班级编号获取班级名称
create or replace function func_class(f_id number) return varchar2 as
v_name t_class.cname%type;
begin
select cname into v_name from t_class where cid = f_id;
return v_name;
exception
when no_data_found then
dbms_output.put_line('找不到数据');
return null;
when others then
dbms_output.put_line('其他异常');
return null;
end;
---调用方法:
---1.通过工具测试
---2.通过plsql块执行
begin
dbms_output.put_line(func_class(1));
end;
---3.通过SQL语句调用【方法中不能有DML操作】
select fun_test1(102) from dual;
select t.*,fun_test1(t.classid) from t_student t
1.3 包 以及包体
--------------------------------------------------包------------------------------------------------
create or replace package p_studentpackage as
procedure insert_student(
p_id t_student.id%type,
p_name t_student.name%type
);
function getname(
p_id t_student.id%type
)return varchar2;
end p_student;
---------------------------------------------------包体(是对包的实现)-------------------------------
create or replace package body p_studentpackage as
procedure insert_student(
p_id t_student.id%type,
p_name t_student.name%type
)is
begin
insert into t_student(id,name)values(seq_t_student.nextval,p_name);
commit;
end;
function getname1(
p_id t_student.id%type
)return varchar2
is
v_name t_student.name%type;
begin
select name into v_name from t_student where id = p_id;
return v_name;
end;
function getname(
p_id t_student.id%type
)return varchar2
is
v_name t_student.name%type;
begin
select name into v_name from t_student where id = p_id;
return getname1(p_id);
end;
end p_student;
1.4 触发器
触发器的定义就是说某个条件成立的时候,触发器里面所定义的语句就会被自动的执行。因此触发器不需要人为的去调用,也不能调用。然后,触发器的触发条件其实在你定义的时候就已经设定好了。这里面需要说明一下,触发器可以分为语句级触发器和行级触发器。详细的介绍可以参考网上的资料,简单的说就是语句级的触发器可以在某些语句执行前或执行后被触发。而行级触发器则是在定义的了触发的表中的行数据改变时就会被触发一次
create [or replace] tigger 触发器名 触发时间 触发事件
on 表名
[for each row]
begin
pl/sql语句
end触发器名:触发器对象的名称。由于触发器是数据库自动执行的,因此该名称只是一个名称,没有实质的用途。
触发时间:指明触发器何时执行,该值可取:
before:表示在数据库动作之前触发器执行;
after:表示在数据库动作之后触发器执行。
触发事件:指明哪些数据库动作会触发此触发器:
insert:数据库插入会触发此触发器;
update:数据库修改会触发此触发器;
delete:数据库删除会触发此触发器。
表 名:数据库触发器所在的表。
for each row:对表的每一行触发器执行一次。如果没有这一选项,则只对整个表执行一次。触发器能实现如下功能:
功能:
1、 允许/限制对表的修改
2、 自动生成派生列,比如自增字段
3、 强制数据一致性
4、 提供审计和日志记录
5、 防止无效的事务处理
6、 启用复杂的业务逻辑
---------------------------------下面的触发器在更新表tb_emp之前触发,目的是不允许在周末修改表:--------------------
create or replace trigger auth_secure before insert or update or DELETE
on tb_emp
begin
IF(to_char(sysdate,'DY')='星期日') THEN
RAISE_APPLICATION_ERROR(-20600,'不能在周末修改表tb_emp');
END IF;
END;
-------------------------------------使用触发器实现序号自增------------------------------------------------------
create table tab_user(
id number(11) primary key,
username varchar(50),
password varchar(50)
);
---创建序列
create sequence my_seq increment by 1 start with 1 nomaxvalue nocycle cache 20;
-----创建触发器-----
CREATE OR REPLACE TRIGGER MY_TGR
BEFORE INSERT ON TAB_USER
FOR EACH ROW--对表的每一行触发器执行一次
DECLARE
NEXT_ID NUMBER;
BEGIN
SELECT MY_SEQ.NEXTVAL INTO NEXT_ID FROM DUAL;
:NEW.ID := NEXT_ID; --:NEW表示新插入的那条记录
END;
---调用----
begin
insert into tab_user(username,password) values('admin','admin');
insert into tab_user(username,password) values('fgz','fgz');
insert into tab_user(username,password) values('test','test');
end;
------------------------------------------------------当用户对test表执行DML语句时,将相关信息记录到日志表-------------------
--创建测试表
CREATE TABLE test(
t_id NUMBER(4),
t_name VARCHAR2(20),
t_age NUMBER(2),
t_sex CHAR
);
--创建记录测试表
CREATE TABLE test_log(
l_user VARCHAR2(15),
l_type VARCHAR2(15),
l_date VARCHAR2(30)
);
--创建触发器
CREATE OR REPLACE TRIGGER TEST_TRIGGER
AFTER DELETE OR INSERT OR UPDATE ON TEST
DECLARE
V_TYPE TEST_LOG.L_TYPE%TYPE;
BEGIN
IF INSERTING THEN
--INSERT触发
V_TYPE := 'INSERT';
DBMS_OUTPUT.PUT_LINE('记录已经成功插入,并已记录到日志');
ELSIF UPDATING THEN
--UPDATE触发
V_TYPE := 'UPDATE';
DBMS_OUTPUT.PUT_LINE('记录已经成功更新,并已记录到日志');
ELSIF DELETING THEN
--DELETE触发
V_TYPE := 'DELETE';
DBMS_OUTPUT.PUT_LINE('记录已经成功删除,并已记录到日志');
END IF;
INSERT INTO TEST_LOG
VALUES
(USER, V_TYPE, TO_CHAR(SYSDATE, 'yyyy-mm-dd hh24:mi:ss')); --USER表示当前用户名
END;
/
--下面我们来分别执行DML语句
INSERT INTO test VALUES(101,'zhao',22,'M');
UPDATE test SET t_age = 30 WHERE t_id = 101;
DELETE test WHERE t_id = 101;
--然后查看效果
SELECT * FROM test;
SELECT * FROM test_log;
---------------------------创建触发器,它将映射emp表中每个部门的总人数和总工资------------------------
--创建映射表
CREATE TABLE dept_sal AS
SELECT deptno, COUNT(empno) total_emp, SUM(sal) total_sal
FROM scott.emp
GROUP BY deptno;
--创建触发器
CREATE OR REPLACE TRIGGER EMP_INFO
AFTER INSERT OR UPDATE OR DELETE ON scott.EMP
DECLARE
CURSOR CUR_EMP IS
SELECT DEPTNO, COUNT(EMPNO) AS TOTAL_EMP, SUM(SAL) AS TOTAL_SAL FROM scott.EMP GROUP BY DEPTNO;
BEGIN
DELETE DEPT_SAL; --触发时首先删除映射表信息
FOR V_EMP IN CUR_EMP LOOP
--DBMS_OUTPUT.PUT_LINE(v_emp.deptno || v_emp.total_emp || v_emp.total_sal);
--插入数据
INSERT INTO DEPT_SAL
VALUES
(V_EMP.DEPTNO, V_EMP.TOTAL_EMP, V_EMP.TOTAL_SAL);
END LOOP;
END;
--对emp表进行DML操作
INSERT INTO emp(empno,deptno,sal) VALUES('123','10',10000);
SELECT * FROM dept_sal;
DELETE EMP WHERE empno=123;
SELECT * FROM dept_sal;
------------------------------------创建触发器,用来记录表的删除数据-------------------------------------
--创建表
CREATE TABLE employee(
id VARCHAR2(4) NOT NULL,
name VARCHAR2(15) NOT NULL,
age NUMBER(2) NOT NULL,
sex CHAR NOT NULL
);
--插入数据
INSERT INTO employee VALUES('e101','zhao',23,'M');
INSERT INTO employee VALUES('e102','jian',21,'F');
--创建记录表(包含数据记录)
CREATE TABLE old_employee AS SELECT * FROM employee;
--创建触发器
CREATE OR REPLACE TRIGGER TIG_OLD_EMP
AFTER DELETE ON EMPLOYEE
FOR EACH ROW --语句级触发,即每一行触发一次
BEGIN
INSERT INTO OLD_EMPLOYEE VALUES (:OLD.ID, :OLD.NAME, :OLD.AGE, :OLD.SEX); --:old代表旧值
END;
/
--下面进行测试
DELETE employee;
SELECT * FROM old_employee;
---------------------------------创建触发器,利用视图插入数据---------------------------
--创建表
CREATE TABLE tab1 (tid NUMBER(4) PRIMARY KEY,tname VARCHAR2(20),tage NUMBER(2));
CREATE TABLE tab2 (tid NUMBER(4),ttel VARCHAR2(15),tadr VARCHAR2(30));
--插入数据
INSERT INTO tab1 VALUES(101,'zhao',22);
INSERT INTO tab1 VALUES(102,'yang',20);
INSERT INTO tab2 VALUES(101,'13761512841','AnHuiSuZhou');
INSERT INTO tab2 VALUES(102,'13563258514','AnHuiSuZhou');
--创建视图连接两张表
CREATE OR REPLACE VIEW tab_view AS SELECT tab1.tid,tname,ttel,tadr FROM tab1,tab2 WHERE tab1.tid = tab2.tid;
--创建触发器
CREATE OR REPLACE TRIGGER TAB_TRIGGER
INSTEAD OF INSERT ON TAB_VIEW
BEGIN
INSERT INTO TAB1 (TID, TNAME) VALUES (:NEW.TID, :NEW.TNAME);
INSERT INTO TAB2 (TTEL, TADR) VALUES (:NEW.TTEL, :NEW.TADR);
END;
/
--现在就可以利用视图插入数据
INSERT INTO tab_view VALUES(106,'ljq','13886681288','beijing');
--查询
SELECT * FROM tab_view;
SELECT * FROM tab1;
SELECT * FROM tab2;
--------------------------------------创建触发器,比较emp表中更新的工资---------------------
-创建触发器
set serveroutput on;
CREATE OR REPLACE TRIGGER SAL_EMP
BEFORE UPDATE ON EMP
FOR EACH ROW
BEGIN
IF :OLD.SAL > :NEW.SAL THEN
DBMS_OUTPUT.PUT_LINE('工资减少');
ELSIF :OLD.SAL < :NEW.SAL THEN
DBMS_OUTPUT.PUT_LINE('工资增加');
ELSE
DBMS_OUTPUT.PUT_LINE('工资未作任何变动');
END IF;
DBMS_OUTPUT.PUT_LINE('更新前工资 :' || :OLD.SAL);
DBMS_OUTPUT.PUT_LINE('更新后工资 :' || :NEW.SAL);
END;
/
--执行UPDATE查看效果
UPDATE emp SET sal = 3000 WHERE empno = '7788';
-------------------------------创建触发器,将操作CREATE、DROP存储在log_info表---------------
--创建表
CREATE TABLE log_info(
manager_user VARCHAR2(15),
manager_date VARCHAR2(15),
manager_type VARCHAR2(15),
obj_name VARCHAR2(15),
obj_type VARCHAR2(15)
);
--创建触发器
set serveroutput on;
CREATE OR REPLACE TRIGGER TRIG_LOG_INFO
AFTER CREATE OR DROP ON SCHEMA
BEGIN
INSERT INTO LOG_INFO
VALUES
(USER,
SYSDATE,
SYS.DICTIONARY_OBJ_NAME,
SYS.DICTIONARY_OBJ_OWNER,
SYS.DICTIONARY_OBJ_TYPE);
END;
/
--测试语句
CREATE TABLE a(id NUMBER);
CREATE TYPE aa AS OBJECT(id NUMBER);
DROP TABLE a;
DROP TYPE aa;
--查看效果
SELECT * FROM log_info;
--相关数据字典-----------------------------------------------------
SELECT * FROM USER_TRIGGERS;
--必须以DBA身份登陆才能使用此数据字典
SELECT * FROM ALL_TRIGGERS;SELECT * FROM DBA_TRIGGERS;
--启用和禁用
ALTER TRIGGER trigger_name DISABLE;
ALTER TRIGGER trigger_name ENABLE;