Oracle学习

it2023-03-28  74

Oracle学习

补充:(查询IP地址)

查询IP地址:win+R,调出窗口,输入cmd,接着输入ipconfig

一、表空间、用户和表

1、表空间

(1)创建表空间

create tablespace MM datafile 'E:\shujuku\tablespace\MM.dbf' size 100m autoextend on next 10m;

(2)删除表空间

drop tablespace MM;

2、用户

(1)创建用户

create user MM identified by MM default tablespace MM;

(2)给用户授权

grant dba to MM;

connect:基本角色;resource:开发者角色;dba:超级管理员角色。

(3)切换用户

session-----------log off,log on

补充内容:数据类型

(1)varchar2和char区别:

varchar2是可变长度,char是定长

(2)varchar和varchar2区别:

目前没有区别,但官方文档不支持使用varchar,强烈建议使用varchar2。

varchar2是oracle提供的独特的数据类型,oracle保证在任何版本中该数据类型向上和向下兼容。但不保证varchar,这是因为varchar是标准sql提供的数据类型,有可能随着sql标准的变化而改变。如果数据库不移植的话,就没有区别。如果数据要移植到别的数据库,可能用varchar比较好。varchar2很多数据库不支持。varchar2把所有字符都占两字节处理(一般情况下),varchar只对汉字和全角等字符占两字节,数字,英文字符等都是一个字节。varchar2把空串等同于null处理,而varchar仍按照空串处理。varchar2字符要用几个字节存储,要看数据库使用使用的字符集,比如GBK,汉字就会占两个字节,英文1个,如果是UTF-8,汉字一般占3个字节,英文还是1个。

3、表

(1)创建表

Create table person( pid number(20), pname varchar2(10) );

可以直接在创建语句后面追加“tablespace name”进行表空间指定。

sql: create table tablename(id int) tablespace tablespacename;

解释:在表空间“tablespacename”上创建表“tablename”。以上语句就实现了为表指定表空间。

(2)修改表结构

1)列操作
添加一列 alter table person add (gender number(1));

注意:()里面可以写很多列,用“,”隔开。

修改列类型 alter table person modify gender char(1); 修改列名称 alter table person rename column gender to sex; 删除一列 alter table person drop column sex;
2)记录操作
添加一条记录 insert into person (pid,pname) values (1,'小明'); commit;

注意1:如果添加的数据占据了所有列,那person后面的()中可以不写内容。

注意2:增删改一定要写commit提交事务;查询不涉及事务。

修改一条记录 update person set pname='小马' where pid = 1; commit;
3)三种删除方式
删除表中全部记录 delete from person; 删除表结构 drop table person; 先删除表,再次创建表,效果等同于删除表中全部记录 truncate table person;

注意1:在数据量大的情况下,尤其是在表中带有索引的情况下,第三种删除方式操作效率高;因为它是先删除索引。

注意2:索引会提高查询效率,但是会影响增删改效率;

(3)查看表

对着表格右击view,column

(4)序列

序列不真的属于任何一张表,但是可以逻辑和表做绑定。

序列的使用:默认从1开始,依次递增,主要用来给主键赋值使用。

dual:虚表,只是为了补全语法,没有任何意义(因为oracle查询必须要打上from)

create sequence s_person; select s_person.nextval from dual; insert into person (pid,pname) values (s_person.nextval,'小明'); commit;

注意:用s_person.currval容易报错,因为第一次使用时,没有当前序列。

补充:(密码重置)

1、Scott用户,密码tiger

2、解锁Scott用户,解锁密码(也可以用来重置密码)

alter user scott account unlock; alter user scott identified by tiger;

注意:此句可以用来重置密码。

补充:(三张表)

EMP:EMPNO(员工编号),ENAME(员工姓名),JOB(员工工作),MGR(员工直属领导编号),HIRDATE(入职日期),SAL(工资),COMM(一年奖金),DEPTNO(部门编号)

DEPT:DEPTNO(部门编号),DNAME(部门名称),LOC(部门地址)

SALGRADE:GRADE(工资等级),LOSAL(最低工资),HISAL(最高工资)

注意1:DEPTNO在EMP中是外键,主键在DEPT中

注意2:右击bonus表,点击query data,表中是没有数据的

二、查询函数

1、单行函数

作用于一行,返回一个值

(1)字符函数

1)小写变大写
select upper('yes') from dual; 查询雇员姓名内容为smith的全部员工: SELECT * FROM emp WHERE ename=UPPER('smith');
2)大写变小写
select lower('YES') from dual;
3)首字母大写函数
select initcap('hello world')from dual; 将雇员表中的全部雇员信息的首字母大写。 SELECT initcap(lower(ename)) FROM emp ;
4)字符串连接函数
select concat('hello','world') from dual; 或者: select 'hello' || 'world' from dual;
5)字符串截取函数

截取的时候需要注意:要从哪里开始截取,之后取多长的长度。

select substr('hello',1,2) from dual; select substr('hello',-3,2) from dual; 倒数第三个开始取两位; 检索姓名最后一个字母为N的雇员: SELECT * FROM emp WHERE substr(ename,-1,1)='N'; 检索职务为“SALE”的全部员工信息 SELECT * FROM emp WHERE substr(job,1,4)='SALE';
6)字符串替换函数
select replace('hello','l','x') from dual;
7)获取字符串长度函数
SELECT length(ename) FROM emp ;

注意:其中length()函数获取的是字符串的字符数,lengthb()获取的是字符串的字节数。但是需要注意的是如果你的数据库中的字段是clob,使用lengthb()会报错。

在oracle 中

select lengthb(‘你好’) from dual; 返回的值是 4

select length(‘你好’) from dual; 返回的值是2

8)instr()函数:查找在一个字符串中是否有指定的字符串

如果有此字符串,则返回位置;如果没有,则返回0。

select instr('hello','o') from dual; SELECT instr('hello','x') FROM dual ;
9)trim()函数:去掉左右空格的函数
select trim(' hello ') from dual;

(2)数值函数

1)四舍五入函数
select round(26.18) from dual; 结果为26; select round(26.18,1) from dual; 结果为26.2; select round(26.16,-1) from dual; 结果为30; select round(26.16,-2) from dual; 注意:此时无法向前进位了,所以结果是0; select round(56.16,-2) from dual; 此时结果为100;
2)截取函数
select trunc(56.16) from dual; 结果为56; select trunc(56.16,1) from dual; 结果为56.1; select trunc(56.16,-1) from dual; 结果为50,直接截取,不再看后面的数字是否大于5;
3)取余函数
select mod(10,3) from dual; 结果为1;

(3)日期函数

1)sysdate(系统时间):

取得当前的日期;

SELECT sysdate FROM dual ;

注意:日期-数字=日期;日期+数字=日期;日期-日期=数字(多少天) (日期可以直接加减,最终的单位是天)

查询emp表中所有员工入职距离现在几天: select sysdate-e.hiredate from emp e; 算出明天此刻: select sysdate+1 from dual; 此时出现的结果是一个日期。 计算员工进入公司的周数: SELECT ename,round((sysdate-hiredate)/7) from emp ; 当前日期-雇佣日期(hiredate) = 天数,天数/7为周数。
2)months_between()函数:

给出指定日期范围的月数;

注意:只有月有months_between。

查询emp表中所有员工入职距离现在几月: select months_between(sysdate,e.hiredate) from emp e; SELECT ename,round(MONTHS_BETWEEN(sysdate,hiredate)) from emp ; 查询emp表中所有员工入职距离现在几年: select months_between(sysdate,e.hiredate)/12 from emp e; 因为一年一定有12个月;
3)add_months()函数:

在指定的日期上加上指定的月数,求出之后的日期。

求出三个月之后的日期是哪一天: select add_months(sysdate,3) from dual;
4)NEXT_DAY():

表示下一个今天(星期几)。

SELECT NEXT_DAY(sysdate,'星期一') FROM dual ; 表示下一个星期一是多少号。 select next_day(sysdate,'Monday') from dual; 注意:可能是由于字符集的原因,这里只能输入Monday,输入‘星期一’一直在报错。。。
5)LAST_DAY:

求出当前日期所在月的最后一天。

SELECT LAST_DAY(sysdate) FROM dual ; 表示本月的最后一天的日期。

(3)转化函数(重点)

1)to_char()函数:转换成字符串
select to_char(sysdate,'yyyy-mm-dd hh:mi:ss') from dual; 注意:因为引号里面不区分大小写,所以分钟不能用mm,否则会和月份无法区分; select to_char(sysdate,'fm yyyy-mm-dd hh:mi:ss') from dual;(去除自动补零机制) select to_char(sysdate,'fm yyyy-mm-dd hh24:mi:ss') from dual;(24小时制) select empno,ename,to_char(hiredate,'yyyy') year,to_char(hiredate,'mm')months,to_char (hiredate,'dd') day from emp;(拆分年月日) select empno,to_char(sal ,'$99,999') from emp; 注意:9表示1位数字,如果是本地货币则用L表示(L99,999) 补充代码: select * from sut where to_char(stu,'yyyymmdd hh24:mi:ss')='20120520 13:20:20';
2)to_number()函数:转换成数字
select to_number('123')+to_number('321') from dual;(字符串转为数字进行相加)
3)to_date()函数:转换成日期
select to_date('2020-9-28 14:24:5','fm yyyy-mm-dd hh24:mi:ss') from dual;

(4)通用函数

1)NVL()函数:将一个指空值变为一个指定的内容
算出emp表中所有员工的年薪: select e.sal*12 from emp e; 奖金里面有null值,如果null值和任意数字做运算,结果都是null; select e.sal*12+nvl(e.comm,0) from emp e; select empo,ename,NVL(comm,0),(sal+NVL(comm,0))*12 income from emp; 如果奖金为空则按零显示再进行计算。
2)decode函数:类似于IF ELSE IF ELSE 语句
select decode(1,1,'内容为1',2,'内容为2',3,'内容为3') from dual; 如果给出的值为1,那么则显示内容为1 ,为2则显示内容为2…… 如果工作为销售则显示销售员、若为业务员则显示业务员、若为总裁则显示总裁: select decode(job,'SALESMAN','销售员','CLERK','业务员','PRESIDENT','总裁') from emp; 职位必须要大写。

(5)综合内容

1、日期转换成数字 首先需要把日期转化为字符 to_char(sysdate,'yyyymmdd') to_number(to_char(sysdate,'yyyymmdd')) 2、 1)取周的开始时间和结束时间: 开始时间(以星期一为开始时间): select trunc(sysdate,'D')+1 from dual; 显示的是一个日期; 结束时间(以星期日为结束时间): select trunc(sysdate,'D')+7 from dual; 2)取月的开始时间和结束时间: 月初时间: select trunc(sysdate,'MM') from dual; select trunc(sysdate,'month') from dual; 月末时间: select last_day(sysdate) from dual; 但是会加上具体的时间; select last_day(trunc(sysdate,'month')) from dual; 或者: select add_months(trunc(sysdate,'month'),1) -1 from dual; 这样不会加上具体时间。 3)取季的开始时间和结束时间: 季初时间:select trunc(sysdate,'Q') from dual; 季末时间:select add_months(trunc(sysdate,'Q'),3)-1 from dual; 4)取年的开始时间和结束时间: 年初时间:select trunc(sysdate,'yyyy') from dual; 年末时间:select add_months(trunc(sysdate,'yyyy'),12)-1 from dual; 3、 SELECT To_char(Trunc(SYSDATE), 'yyyy/mm/dd hh24:mi:ss') FROM dual; SELECT To_date(To_char(Trunc(SYSDATE), 'yyyy/mm/dd hh24:mi:ss'), 'yyyy/mm/dd hh24:mi:ss') FROM dual 第一句出来的是varchar类型,不管哪个数据库平台或者工具平台,你都能得到后面带 00:00:00 的结果。 第二句得到的是Date类型 4、 根据月份获取所在季度 SELECT FLOOR((TO_NUMBER('05') - 1) / 3) + 1 FROM DUAL; 注意:floor()函数是向下取整; 获取当前日期所在季度的第一天 SELECT TRUNC(SYSDATE, 'Q') FROM DUAL; 获取指定日期所在季度的第一天 SELECT TRUNC(to_date('202009','yyyyMM'), 'Q') FROM DUAL; 获取指定日期所在季度的第一个月 select to_char(TRUNC(to_date('202009','yyyyMM'), 'Q'), 'mm') from dual; 5、 取这个季度的第一个月的月末: select last_day(trunc(to_date('202009','yyyymm'), 'q')) from dual; 查20年9月28号上午11点28分30秒 select to_date('2020-09-28 11:28:35','yyyy-mm-dd hh:mi:ss') from dual; 然后查一个当前时间往后推10分钟 select sysdate+1/(24*60)*10 from dual; 我要取一个字符串“我是王小二”,截取成“王小二” select substr('我是王小二',-3) from dual; 如果只要王: select substr('我是王小二',-3,1) from dual; 当前时间的月转成数字 select substr(to_number(to_char(sysdate,'yyyymmdd')),5,2) from dual; 不用substr呢 select to_number(to_char(sysdate,'mm')) from dual; 取当前时间的月初,月末,今天0点,年初,直接写一句 select trunc(sysdate,'mm'), to_char(last_day(sysdate),'yyyy/mm/dd'), to_char(trunc(sysdate), 'yyyy/mm/dd hh24:mi:ss'), trunc(sysdate,'yyyy') from dual; 6、 select trunc(sysdate) from dual; 结果:2020/10/19

(6)补充内容的网址

修改货币字符集的参考网址:

https://www.cnblogs.com/huak/p/3448928.html

oracle中的trunc函数:

https://www.cnblogs.com/williamwsj/p/7001067.html

oracle中的to_char函数:

https://www.cnblogs.com/aipan/p/7941917.html

2、条件表达式:

(1)oracle和mysql通用写法

1)给emp表中员工起中文名
select e.ename, case e.ename when 'SMITH' then '史密斯' when 'ALLEN' then '艾伦' when 'WARD' then '沃德' else '无名' end from emp e;

注意1:只要case后面不加“,”,就一直在一列之中;

注意2:When then可以不限数量写很多个,else可以省略

注意3:注释一行语句用“——”

2)判断emp表中员工工资:

如果高于3000显示高收入,高于1500低于3000显示中等收入,其余显示低收入

select e.sal, case when e.sal>3000 then '高收入' when e.sal>1500 then '中等收入' else '低收入' end from emp e;

(2)oracle专用条件表达式

select e.ename, decode(e.ename, 'SMITH', '史密斯', 'ALLEN', '艾伦', 'WARD', '沃德', '无名')中文名 from emp e; 或者: select e.ename, decode(e.ename, 'SMITH', '史密斯', 'ALLEN', '艾伦', 'WARD', '沃德', '无名')"中文名" from emp e;

总结:oracle中除了起别名,都加单引号。(起别名可以加双引号或者不加引号)

3、多行函数

(即聚合函数:作用于多行,返回一个值)

(1)查询总数量

select count(1) from emp;

注意:写count(*)也行,但最好写count(1),相当于count(empno),即count主键这一列。

(2)求和,最大值,最小值,平均数

select sum(sal) from emp; select max(sal) from emp; select min(sal) from emp; select avg(sal) from emp;

注意:分号是一次查询的结束。

4、分组查询

分组查询不要一次性全部写完:select。。。from。。。group by。。。

(1)查询出每个部门的平均工资

select e.deptno,avg(e.sal) from emp group by e.deptno;

注意:分组查询中,出现在group by后面的原始列,才能出现在select后面; 没有出现在group by后面的列,想在select后面出现,必须加上聚合函数。

比如:

select e.deptno,avg(e.sal),ename from emp e group by e.deptno;就会出现报错;

主要是因为用了分组查询,表结构就会发生改变,由14行变成了3行,但是每个部门肯定不止一个人;

聚合函数有一个特性,可以将多行记录变成一个值;

(2)查询出平均工资高于2000的部门信息

select e.deptno,avg(e.sal) from emp e group by e.deptno having avg(e.sal)>2000; 或者: select e.deptno,avg(e.sal) asal from emp e group by e.deptno having avg(e.sal)>2000; 但是: select e.deptno,avg(e.sal) asal from emp e group by e.deptno having asal>2000;是不可以的 所有条件都不能使用别名来判断 select ename, sal s from emp where sal>1500; select ename, sal s from emp where s>1500;(此时会报错) (因为查询时都是先看条件,先看where后面的,然后才select)

(3)查询出每个部门工资高于800的员工的平均工资

select e.deptno, avg(e.sal) asal from emp e where e.sal>800 group by e.deptno;

注意:where是过滤分组前的数据,having是过滤分组后的数据

表现形式:where必须在group by之前,having在group by之后

(4)查询出每个部门工资高于800的员工的平均工资,再查询出平均工资高于2000的部门信息

select e.deptno, avg(e.sal) asal from emp e where e.sal>800 group by e.deptno having avg(e.sal)>2000;

5、多表查询

(1)笛卡尔积

select * from emp e, dept d;

emp表中14条记录,dept表中4条记录,一共14*4条。

注意:笛卡尔积中大部分数据都是没有用的

(2)等值连接

select * from emp e, dept d where e.deptno=d.deptno;

(3)内连接

select * from emp e inner join dept d on e.deptno=d.deptno;

效果和等值连接一样。

(4)外连接

查询出所有的部门,以及该部门下的员工信息: select * from emp e right join dept d on e.deptno=d.deptno; 这样查询出来,将会多一条空部门的记录 查询所有员工信息,以及员工所属部门: select * from emp e left join dept d on e.deptno=d.deptno; 这样查询出来,是14条记录 和inner join是有区别的,当有的员工没有部门的时候,就会显现出区别了

(5)oracle中专用外连接

显示部门表全部数据 查询出所有的部门,以及该部门下的员工信息: select * from emp e, dept d where e.deptno(+)=d.deptno; 一共15条 查询所有员工信息,以及员工所属部门: select * from emp e, dept d where e.deptno=d.deptno(+); 此时一共14条

(6)自连接:

其实就是站在不同的角度将一张表看成多张表

1)查询出员工姓名,员工领导姓名 select e1.ename,e2.ename from emp e1, emp e2 where e1.mgr=e2.empno;--e1表是员工表,e2表是领导表 一共有13条,因为有一个员工没有领导 2)查询出员工姓名,员工部门名称,员工领导姓名,员工领导部门名称 select e1.ename,d.dname,e2.ename,d.dname from emp e1, emp e2, dept d where e1.mgr=e2.empno and e1.deptno=d.deptno and e2.deptno=d.deptno 此时语法没问题,但是只有11条记录,因为加的过滤条件是:员工和领导在同一个部门。。。 所以正确写法: select e1.ename,d1.dname,e2.ename,d2.dname from emp e1, emp e2, dept d1, dept d2 where e1.mgr=e2.empno and e1.deptno=d1.deptno and e2.deptno=d2.deptno 3)查询出每个员工编号,姓名,部门名称,工资等级和他的上级领导姓名,工资等级 select e.empno, e.ename, e.sal, decode (s.grade,1,'一级',2,'二级',3,'三级',4,'四级',5,'五级') e_grade, d.dname, e1.empno, e1.ename, e1.sal, decode (s1.grade,1,'一级',2,'二级',3,'三级',4,'四级',5,'五级') e1_grade from emp e, emp e1, dept d, salgrade s, salgrade s1 where (e.mgr = e1.empno) and (e.deptno = d.deptno) and (e.sal between s.losal and s.hisal) and (e1.sal between s1.losal and s1.hisal);

6、子查询

1)子查询返回一个值

查询出工资和SCOTT一样的员工信息 select * from emp where sal = (select sal from emp where ename = 'SCOTT');

注意1:SCOTT必须写大写,不可以写小写

注意2:select * from emp where sal =

(select sal from emp where ename = ‘SCOTT’);

这里的等于最好写in,因为此时ename并不是主键,所以可能会有很多SCOTT,但是如果ename是主键,可以写“=”。

2)子查询返回一个集合

查询出工资和10号部门任意员工一样的员工信息 select * from emp where sal in (select sal from emp where deptno = 10);

3)子查询返回一张表

查询出每个部门最低工资,最低工资员工姓名,和该员工所在部门名称 步骤1:先查询出每个部门最低工资 select deptno,min(sal) msal from emp group by deptno; 步骤2:三表联查,得到最终结果 select t.deptno, t.msal, e.ename, d.dname from (select deptno,min(sal) msal from emp group by deptno) t, emp e, dept d where t.deptno = e.deptno and t.msal = e.sal and e.deptno = d.deptno;

注:子查询永远先执行里面,所以这里可以使用别名

7、分页查询

1)rownum行号

当我们做select操作的时候,每查询出一行记录,就会在该行上加上一个行号,行号从1开始,依次递增,不能跳着走。

排序操作会影响rownum的顺序 select rownum,e.* from emp e order by e.sal desc

注1:rownum前面不能加表的别名

注2:rownum只能放在select后面

注3::此时,rownum的顺序是乱的,因为必须把所有数据查询了之后,才能排序;

​ 先执行select,此时rownum顺序是正确的, 加上order by之后,就成了乱序;

如果涉及到排序,但还要使用rownum的话,我们可以再次嵌套查询 select rownum, t.* from( select rownum,e.* from emp e order by e.sal desc) t emp表工资倒叙排列后,每页五条记录,查询第二页: 错误代码: select rownum, e.* from(select * from emp order by sal desc) e where rownum<11 and rownum>5

此时一条记录都没有了,因为行号是从1开始,不能跳着走

并且,是先where,后select,1肯定小于5,所以永远无法满足大于5的条件,所以没有办法select;

结论:条件中rownum行号不可以写大于一个正数(>0是可以的)

正确代码: select * from(select rownum rn, e.* from(select * from emp order by sal desc) e where rownum<11) where rn>5; 此时,将rownum变成普通的一列 还有一种写法: select * from (select rownum r, e.* from emp e) b where b.r>5 and b.r<11; 但是这种写法不可以order by 操作

8、视图

1)视图的概念:

视图就是提供一个查询的窗口,所有数据来自于原表。

2)查询语句创建表

create table emp as select * from scott.emp 可以跨用户查询 select * from emp

3)创建视图(必须有dba权限)

create view v_emp as select ename, job from emp;

注:视图需要起名字。

4)查询视图

select * from v_emp;

5)修改视图(不推荐)

update v_emp set job='CLERK' Where ename='ALLEN'; commit;

注意:此时原表的数据也改变了,视图中根本没有存放数据,改的是表的数据。

6)创建只读视图

create view v_emp1 as select ename, job from emp with read only;

7)视图的作用?

1)视图可以屏蔽掉一些敏感字段

2)保证总部和分部数据及时统一

9、索引

1)索引的概念:

索引就是在表的列上构建一个二叉树,达到大幅度提高查询效率的目的,但是索引会影响增删改的效率

2)单列索引

创建单列索引 create index idx_ename on emp(ename); 单列索引触发规则,条件必须是索引列中的原始值。

(单行函数,模糊查询,都会影响索引的触发)

select * from emp where ename='SCOTT';

3)复合索引

创建复合索引 create index idx_enamejob on emp(ename,job);

复合索引中第一列为优先检索列;

如果要触发复合索引,必须包含有优先检索列中的原始值。

a)select * from emp where ename='SCOTT'; 注:这一列既包含在单列索引中,又包含在复合索引中,此时触发的是单列索引; b)select * from emp where ename='SCOTT' and job='xx'; 此时触发的是复合索引; c)select * from emp where ename='SCOTT' or job='xx'; 此时不触发索引; 因为写了or,就相当于写了两个查询语句,一个触发,一个不触发,最终的结果就是不触发。

三、PL/SQL编程语言

pl/sql编程语言是对sql语言的扩展,使得sql语言具有过程化编程的特性。

pl/sql编程语言比一般的过程化编程语言,更加灵活高效。

pl/sql编程语言主要用来编写存储过程和存储函数等。

1、定义变量

1)声明方法

a)

declare i number(2) := 10; s varchar2(10) := '小明'; begin dbms_output.put_line(i); dbms_output.put_line(s); end;

i定义一个变量,number表示长度;

一般都用number,因为防止i是一个小数。

b)

declare ena emp.ename%type;--------引用型变量(变量类型为emp表中ename的类型) empow emp%rowtype;---------记录型变量(即存一行记录,一个对象) begin select ename into ena from emp where empno=7788; dbms_output.put_line(ena); select * into empow from emp where empno=7788; dbms_output.put_line(empow.ename || '的工作为:' || empow.job); end;

注意:赋值操作可以使用“:=”,也可以使用into查询语句赋值;oracle中的连接符号为“||”。

2、pl/sql中的if判断

输入小于18的数字,输出未成年; 输入大于18小于40的数字,输出中年人; 输入大于40的数字,输出老年人; declare i number(3) :=&ii; begin if i<18 then dbms_output.put_line('未成年'); elsif i<40 then dbms_output.put_line('中年人'); else dbms_output.put_line('老年人'); end if; end;

3、pl/sql中的loop循环

(三种循环中用的比较多的是exit循环)

e.g:用三种方式输出1到10十个数字:

1)while循环
declare i number(2) := 1; begin while i<11 loop dbms_output.put_line(i); i := i+1; end loop; end;

注意:写的时候一定要严格注意分号。

2)exit循环(退出循环)
declare i number(2) :=1; begin loop exit when i>10; dbms_output.put_line(i); i := i+1; end loop; end;
3)for循环
begin for i in 1..10 loop dbms_output.put_line(i); end loop; end;

4、pl/sql中的游标

可以存放多个对象,多行记录

1、输出emp表中所有员工的姓名 declare cursor c1 is select * from emp; emprow emp%rowtype; begin open c1; loop fetch c1 into emprow; exit when c1%notfound; dbms_output.put_line(emprow.ename); end loop; close c1; end; 2、给指定部门员工涨工资 declare cursor c2(eno emp.deptno%type)-------带参数的游标 is select empno from emp where deptno = eno; en emp.empno%type; begin open c2(10);----给10号员工涨工资 loop fetch c2 into en; exit when c2%notfound; update emp set sal=sal+100 where empno=en; commit; end loop; close c2; end; 查询10号部门员工工资 select * from emp where deptno=10;

5、存储过程

就是提前已经编译好的一段pl/sql语言,放置在数据库端,可以直接被调用。

这一段pl/sql一般都是固定步骤的业务。

1)语法:

[or replace]可写可不写;是直接修改的意思,一般最好加上,尤其是业务复杂的时候

PROCEDURE是关键字,必须要带上

默认不写的类型是in类型

AS也可以写IS

2)案例

给指定员工涨100块钱工资:

create or replace procedure p1(eno emp.empno%type)---不写,则默认in类型 is begin update emp set sal=sal+100 where empno = eno; commit; end;

测试p1:

declare begin p1(7788); end; select * from emp where empno = 7788;

6、存储函数

1)语法

2)存储过程和存储函数的区别:

一般来讲,过程和函数的区别在于,函数可以有一个返回值,而过程没有返回值;

但过程和函数都可以通过out指定一个或多个输出函数。我们可以利用out参数,在过程和函数中实现返回多个值。

注意:存储过程和存储函数的参数都不能带长度;存储函数的返回值类型不能带长度;

3)案例:

通过存储函数实现计算指定员工的年薪:

create or replace function f_yearsal(eno emp.empno%type) return number------返回值类型后面不可以加长度(引用型变量在不需要长度的时候会自己去掉长度) is s number(10);-----这里是自己定义的变量 begin select sal*12+nvl(comm,0) into s from emp where empno = eno; return s; end;

测试f_yearsal:

declare s number(10); begin s := f_yearsal(7788); dbms_output.put_line(s); end;

注意:存储函数在调用的时候,返回值需要接收

7、out类型参数如何使用

1)使用存储过程来算年薪

create or replace procedure p_yearsal(eno emp.empno%type,yearsal out number) is s number(10); c emp.comm%type; begin select sal*12, nvl(comm,0) into s, c from emp where empno = eno; yearsal := s+c; end;

测试p_yearsal:

declare yearsal number(10); begin p_yearsal(7788, yearsal); dbms_output.put_line(yearsal); end;

2)in和out类型参数的区别是什么?

凡是涉及到into查询语句赋值或者:=赋值操作的参数,都必须使用out来修饰;否则其余的都用in。

8、存储过程和存储函数的区别

1)语法区别

关键字不一样,一个是procedure,一个是function

存储函数比存储过程多了两个return。

本质区别:存储函数有返回值,而存储过程没有返回值;

如果存储过程想实现有返回值的业务,我们就必须使用out类型的参数;

即便是存储过程使用了out类型的参数,其本质也不是真的有了返回值,而是在存储过程内部给out类型参数赋值,在执行完毕之后,我们直接拿到输出类型参数的值。

我们可以使用存储函数有返回值的特性,来自定义函数。

而存储过程不能用来自定义函数。

2)案例需求

查询出员工姓名,员工所在部门名称。

1)案例准备工作: 把SCOTT用户下的dept表复制到当前用户下 create table dept as select * from scott.dept; 2)使用传统方式来实现案例需求: select e.ename,d.dname from emp e,dept d where e.deptno=d.deptno; 3)使用存储函数来实现提供一个部门编号,输出一个部门名称 create or replace function fdna(dno dept.deptno%type) return dept.dname%type is dna dept.dname%type; begin select dname into dna from dept where deptno = dno; return dna; end; 4)使用fdna存储函数来实现案例需求:查询出员工姓名,员工所在部门名称 select e.ename, fdna(e.deptno) from emp e;

注意:只有存储函数可以这样做,因为有返回值。

四、触发器

就是制定一个规则,在我们做增删改操作的时候,只要满足该规则,自动触发,无需调用。

语句级触发器:不包含有for each row的触发器

行级触发器:包含有for each row的就是行级触发器

加for each row是为了使用:old或者:new对象或者一行记录。

1、语句级触发器案例

插入一条记录,输出一个新员工入职

1)create or replace trigger t1 -----操作后触发 after insert on person declare begin dbms_output.put_line('一个新员工入职');-----双引号只在起别名的时候使用 end; 2)触发t1 insert into person values (1, '小红'); commit; select * from person;

2、行级别触发器案例

不能给员工降薪:

1)create or replace trigger t2 -----降工资前触发,拦截降工资这件事情 before update on emp for each row declare begin if :old.sal>:new.sal then raise_application_error(-20001,'不能给员工降薪');---抛异常 end if; end; 注意:raise_application_error(-20001~-20999之间,不能重复,'错误提示信息');---抛异常 3)触发t2 update emp set sal=sal-1 where empno = 7788; commit; select * from emp where empno=7788;

3、触发器实现主键自增 (用到行级触发器)

分析:在用户做插入操作的之前,拿到即将插入的数据,给该数据中的主键列赋值。

1) create or replace trigger auid before insert on person for each row declare begin select s_person.nextval into :new.pid from dual; end; 2)查询person表数据 select * from person; 3)使用auid实现主键自增 insert into person (pname) values ('a'); commit; 或者: insert into person values (1,'b'); commit;

补充:(java调用)

oracle 10g -------ojdbc14.jar

oracle 11g -------ojdbc6.jar

五、案例:打印日历学习

declare v_year number := 2020; v_month number := &input_month; v_day number; v_lastday number; j number(2) :=1; begin dbms_output.put_line(v_year || '年' || v_month || '月的月历'); ---–转换星期为数字,方便计算 case rtrim(to_char(to_date(v_year || v_month,'yyyymm'),'day')) when 'sunday' then v_day := 1; when 'monday' then v_day := 2; when 'tuesday' then v_day := 3; when 'wednesday' then v_day := 4; when 'thursday' then v_day := 5; when 'friday' then v_day := 6; when 'saturday' then v_day := 7; else null; end case; v_lastday := to_char(last_day(to_date(v_year || v_month,'yyyymm')),'dd'); dbms_output.put_line('本月第一天为本周第' || v_day || '天'); dbms_output.put_line('本月共' || v_lastday || '天'); dbms_output.put_line('日 一 二 三 四 五 六'); -----–PS:以上所有要用到的数据都已经得到了,下面是展示 -----–缩进月的第一天 while j<v_day loop dbms_output.put(' '); j:= j+1; end loop; -------–顺序打印每天 for i in 1..v_lastday loop dbms_output.put(to_char(i,'99')||' '); --如果遇到星期六,则换行(保证星期的标识从1-7循环) if v_day = 7 then dbms_output.put_line(' '); v_day := 1; else v_day := v_day + 1; end if; end loop; -----–PS:空格在打印的时候,可能被省略,可以换成其他字符尝试 -----–输出最后一行未能输出的字符 dbms_output.put_line(' '); end;

六、案例:身份证15位转18位

create or replace function f_15to18(idCardNum varchar2) return varchar2 is Result varchar2(20); i smallint; -----整型 num smallint:=0; sNum varchar2(2); -----变长 code char(1); ------定长 temp17 varchar2(20); begin if length(idCardNum)=18 or idCardNum is null then Result:=upper(idCardNum); ------小写字符转换成大写 elsif length(idCardNum)=15 then temp17:=substr(idCardNum,1,6) || '19' || substr(idCardNum,7,9); i:=18; while i>1 loop num:=num+mod(power(2,i-1),11)*substr(temp17,19-i,1) ;-----算出系数乘以位数 i:=i-1; end loop; num:=mod(num,11); sNum:=num || ''; if num=0 then code:='1'; elsif num=1 then code:='0'; elsif num=2 then code:='X'; else code:=(12-num) || ''; end if; Result:=temp17 || code; else Result:= idCardNum; end if; return Result; exception when others then dbms_output.put_line(idCardNum); end f_15to18; declare idCardNum varchar2(20) := &input_id; Result varchar2(20); begin Result := f_15to18(idCardNum); dbms_output.put_line(Result); end;

补充:

身份证号码15位升18位的计算方法:

1、第一步在原15位身份证的第六位后面插入19

这样身份证号码为17位

2、每一位与其对应的系数相乘

系数表:

位数1234567891011121314151617乘数7910584216379105842

乘数的由来:mod(power(2,i-1),11) i从18开始

把相乘之后的结果相加,然后对11取余

得到一个0-10的数字尾数

尾数对应表:

01234567891010X98765432

这样就得出你的18位身份证号

例如,原15位身份证号码为:310112850409522

1)310112850409522 --> 310112 + 19 + 850409522 --> 31011219850409522

2)3*7+1*9+0*10+1*5+1*8+2*4+1*2+9*1+8*6+5*3+0*7+4*9+0*10+9*5+5*8+2*4+2*2 = 258

3)258 Mod 11 = 5

4)5 --> 7

5)31011219850409522 + 7 --> 310112198504095227

故升级后,新18位身份证号码为:310112198504095227

最新回复(0)