1.Oracle体系结构
本次使用的是Oracle18c数据库版本 java学习交流Q群:1004577069
1>数据库
Oracle数据库时数据的物理存储,这就包括(数据库文件ORA或者DBF,控制文件,联机日志,参数文件).其实Oracle数据库的概念和其他数据库不一样,这里的数据库时一个操作系统只有一个库.可以看作是Oracle就只有一个大数据库
2>实例
一个Oracle实例(Oracle Instance)有一系列的后台进程(Background Processes)和内存结构(Memory Stryctures)组成一个数据库可以有N个实例
3>用户
用户时在实例下创建的,不同实例可以创建相同名字的用户
4>表空间
表空间是Oracle对物理数据库上相关数据文件(ORA或者DBF文件)的逻辑映射一个数据库在逻辑被划分成一到若干个表空间,每个表空间包含了在逻辑上相关联的一组结构.每个数据库至少有一个表空间(system表空间)
每个表空间由同一磁盘上的一个或者多个文件组成,这些文件叫数据文件(datafile)一个数据文件只能属于一个表空间
5>数据文件
数据文件是数据库的物理存储单位,数据库的数据是存储在表空间中的,真正是在某一个或者多个数据文件中而一个表空间可以由一个或者多个数据文件组成,一个数据文件只能属于一个表空间。一旦数据文件被加入到某个表空间后,就不能删除这个文件,如果要删除某个数据文件,只能删除其所属于的表空间才行
注:表的数据,是有用户放入到某一个表空间的,而这个表空间会随机把这些数据放到一个或者多个数据文件中
由于oracle数据库不是普通的概念,oracle是有用户和表空间数据进行管理和存放的但是表不是由表空间去查询的,而是用户去查询的因为不同用户可以在同一个表空间建立同一个名字的表,这里区分的就是用户了
2.创建表空间
表空间
表空间:ORACLE数据库的逻辑单元,数据库----表空间 一个表空间可以与多个数据文件(物理结构)关联
一个数据库可以建立多个表空间,一个表空间可以建立多个用户一个用户可以建立多个表
create tablespace itxu datafile 'c:\itxu.dbf' size 100m autoextend on next 10m
itxu:为表空间名称
datafile:指定表空间对应的数据文件
size:后定义的表空间的初始大小
autoextend on:自动增长,当表空间存储都占满,自动增长
next:后指定的是一次增长的大小
3.用户
1.创建用户
create user itxu
identified by '010313'
default tablespace itxu
identified by:后边是用户的密码
default tablespace:后边是表名称空间
oracle数据库与其他是数据库产品区别在于,表和其他的数据库对象都是存储在用户下的
4.用户赋权限
新建的用户没有权限,登录后会提示
Oracle中已存在三个重要的角色: connect角色,resource角色,dba角色
CONNECT角色:是授予最终用户的典型权利,最基本的
ALTER SESSION----修改会话
CREATE CLUSTER----建立聚簇
CREATE DATABASE LINK---建立数据库链接
CREATE SEQUENCE----建立序列
CREATE SESSION----建立会话
CREATE SYNONYM-----建立同义词
CREATE VIEW-----建立视图
RESOURCE角色:是授予开发人员的
CREATE CLUSER-----建立聚簇
CREATE PROCEDURE----建立过程
CREATE SEQUECE----建立序列
CREATE TABLE-----建表
CREATE TRIGGER---建立触发器
CREATE TYPE-----建立类型
BDA角色:拥有全部特权,是系统最高权限,只有BDA才可以创建数据库结构,并且系统权限也需要DBA授出,且DBA用户可以操作全体用户的任意基表,包括删除
grant dba to itxu
进入system用户下给用户赋予dba权限,否则无法登陆
5.Oracle数据类型
1>Varchar,varchar2:表示一个字符串
2>NUMBER:NUMBER(n)表示一个整数,长度是n/NUMBER(m,n):表示一个小数,总长度是m,小数是n,整数是m-n
3>DATA:表示日期类型
4>CLOB:大对象,表示大文本数据类型,可存4G
5>BLOB:大对象,表示二进制数据,可存4G
6.表的管理
1>建立表
create table 表名(
字段1 数据类型 [default 默认值],
字段2 数据类型 [default 默认值],
.....
字段n 数据类型 [default 默认值]
);
范例:创建person表
create table person(
pid number(10),
name varchar2(10),
gender number(1) default 1,
birthday date
);
2>.表删除/修改
表删除
语法:DROP TABLE 表名
drop table person;
表的修改
在sql中使用alter可以修改表
添加语法: ALTER TABLE 表名称 ADD(列名1 类型[DEFAULT 默认值],列名1 类型[DEFAULT 默认值]....);
修改语法: ALTER TABLE 表名称 MODIFY(列名1 类型[DEFAULT 默认值],列名1 类型[DEFAULT 默认值]...)
修改列名: ALTER TABLE 表名称 RENAME 列名1 TO 列名2
范例:在person表中添加address列
alter table person add(address varchar2(10));
范例:在person表deaddress列的长度修改为20
alter table person modify (address varchar2(20));
3>.数据库的CRUD操作
3>数据库表数据的更新
INSERT(添加)
标准写法:INSERT INTO 表名[(列名1,列名2)...]VALUES(值1,值2,....);
简单写法O:INSERT INTO 表名 VALUES(值1,值2...);
注意:使用简单的写法必须按照表中的字段的顺序来插入值,而且如果有为空的字段使用null
insert into person(pid,name,gender,birthday)values(1,'张三',1,NULL);
UPDATE(修改)
全部修改: UPDATE 表名 SET 列名1=值1,列名2=值2,...
局部修改: UPDATE 表名 SET 列名1=值1,列名2=值2,...WHERE 修改条件
DELETE(删除)
语法: DELETE FORM 表名 WHERE 删除条件;
在删除语句中如果不指定删除条件的话就会删除所有的数据,因为ORACLE的事务对数据库的变更处理,我们必须做提交事务才能让数据真正的插入到数据库中,在同样在执行完数据库变更的操作后还可以把事务进行回滚,这样就不会插入到数据库.如果事务提交后则不可以再回滚
提交:COMMIT 回滚:ROLLBACK
4>.序列
4>序列
在很多数据库中都存在一个自动增长的列,如果现在想要在Oracle中完成自动增长的功能,则只能依靠序列完成,所有的自动增长操作需要用户手动完成处理
语法: CREATE SEQUENCE 序列名
[INCREMENT BY n] --默认增加几个
[START WITH n] --序列从几开始默认是1
[{MAXVALUE/MINVALUE n|NOMAXVALUE}]
[{CYCLE|NOCYCLE}]
[{CACHE n|NOCACHE}];
范例:创建一个seqpersonid序列,验证自动增长的操作
CREATE SEQUENCE seqpersonid;
序列创建完成之后,所有的自动增长应该由用户自己处理,所以在序列中提供了以下的两种操作:
nextval:取得序列的下一个内容
currval:取得序列的当前内容
SELECT sqlpersonid.currval from dual;
SELECT sqlpersonid.nextval from dual;
在插入数据时需要自增的主键可以这样使用
insert into person values(sqlpersonid.nextval,'张三',1,null);
在实际项目中每一张表会配一个序列,但是表和序列是没有必然联系的,一个序列被哪一张表都可以使用但是我们一般一张表用一个序列
序列的管理一般使用工具来管理
7.单行函数
1.字符函数
接收字符输入返回字符或者数值,dual是伪表
1).把小写的字符转化为大写的字符
select upper('hello') from dual;
2).把大写字符转化为小写的字符
select lower('HELLO')from dual;
2.数值函数
1).四舍五入函数:ROUND()
默认情况下ROUND四舍五入取整可以自己指定保留的位数
select round(26.18) from dual;
2).直接截取:TRUNC()
直接截取不看后面的数字是否是大于5
select trunc(56.16 -1) from dual;
3).求余数MOD()
select mod(10,3)from dual;
3.日期函数
Oracle中提供了很多和日期相关的函数,包括日期的加减等等
---查询出emp表中所有员工入职距离现在多少天
select sysdate-e.hiredate from emp e;
---算出明天此刻
select sysdate+1 from dual;
---查询出emp表中所有员工入职距离现在几个月
select months_between(sysdate,e.hiredate)from emp e;
---查询出emp表中所有员工入职距离现在几年
select months_between(sysdate,e.hiredate)/12 from emp e;
---查询出emp表中所有员工入职距离现在有多少个礼拜
select (sysdate-e.hiredate)/7 from emp e;
4.转换函数
--日期转字符串
select to_char(sysdate,'fm yyyy-MM-dd hh24:mi:ss')from dual;
--字符串转日期
select to_date(' 2019-7-24 23:53:54','fm yyyy-MM-dd hh24:mi:ss')from dual;
5.通用函数
1).空值处理
select e.sal*12+nvl(e.comm,0) from emp e;
2).Decode函数
该函数类似if...else if...else
语法:DECODE(col/expression,[seach1,result],[seach2,result2])...[default])
Col/expression:列名或表达式
Search1,Search2...用于比较的条件
Result1:返回值
如果col/expression和Searchi匹配就返回resulti,否则返回default的默认值
3).case when
select t.empno,t.ename,
case
when t.job='CLERK'then '业务员'
else
'无业'
end
from emp t;
8.多行函数(聚合函数)
1>.统计记录数COUNT()
查询出员工的数量
select count(*)from emp;
2>.最小值 MIN()
查询员工的最低工资
select min(sal) from emp;
3>.最大值 MAX()
查询员工的最高工资
select max(sal) from emp;
4>.查询平均值AVG()
查询出员工的平均工资
select avg(sal)from emp;
5>.求和函数SUM()
查询出20号部门的员工的工资总和
select sum(sal)from emp t where t.deptno=20;
9.分组统计
分组统计需要使用GROUP BY来分组
语法:SELECT *|列名 FROM 表名{WHERE 查询条件}{GROUP BY分组字段}ORDER BY 列名1 ASC/DESC
1>查询出每个部门的人数
select deptno,count(ename)from emp group by deptno;
2>查询出每个部门的平均工资
select deptno,avg(sal) from emp group by deptno;
3>查询出部门编号,和部门下的人数
select deptno,count(ename)from emp;
ORA-00937:不是单组分组函数
1.如果使用分组函数,SQL只可以把GROUP BY分组条件字段和分组函数查询出来,不能有其他字段
2.如果使用分组函数,不适用GROUP BY只可以查询出来分组函数的值
select deptno,ename,count(ename)from emp group by deptno;
ORA-00979:不是GROUP BY表达式
4>按部门分组,查询出部门名称和部门员工数量
select d.deptno,d.dname,count(ename) from emp e,dept d where e.deptno=d.deptno group by d.deptno,d.dname;
5>查询出部门人数大于5人的部门
select d.deptno,d.name,count(ename)from emp e,dept d where e.deptno=d.deptno group by d.deptno,d.dname having count(ename)>5;
6>查询出部门平均工资大于2000的部门
select deptno,avg(sal)from emp group by deptno having avg(sal)>2000;
10.多表查询
1>多表连接基本查询
使用一张以上的表就是多表查询
语法:SELECT{DISTINCT}* |列名...FROM 表名 别名,表名1 别名{WHERE 限制条件 ORDER BY 排序字段 ASC|DESC}
如果多张表进行一起查询而且每张表的数据很大的话笛卡尔积就会变的非常大,对性能造成影响,想要去掉笛卡尔积我们需要关联查询
在两张表中我们发现有一个共同的字段是deptno,deptno就是两张表的关联的字段,我们可以使用这个字段来做限制条件,两张表的关联查询字段一般是其中一张的表的主键,另外一张表的外键
select * from emp,dept where emp.deptno=dept.deptno;
2>外连接
1).右连接
当我们在做基本的查询的时候,查询出所有的部门下的员工我们发现编号为40的部门下没有员工,但是要求把该部门也展示出来,我们发现上面的基本查询时办不到的
select e.empno,ename,d.deptno,d.dname,from emp e,dept d
where e.deptno(+)=d.deptno;
使用(+)表示左连接或者右连接,当(+)在左边表的关联字段上时是左连接,如果是右边表的关联字段是右连接
查询出所有员工的上级领导
select e.empno,e.ename,m.empno,m.ename from emp e,emp m
where e.mgr=m.empno(+);
11.子查询
子查询:在一个查询的内部还包括着另一个查询,则称为子查询
查询出比编号 7654 薪资高的员工
select * from emp where emp.sal>(select t.sal from emp t where t.empno='7654');
1>.子查询的操作分为三类
单列子查询:返回的结果是一列或者是一个内容
单行子查询:返回多个列,有可能是一个完整的记录
多行子查询:返回多条记录
查询出比员工7654的工资高,同时从事和7788的工作一样的员工
select * from emp e1 where e1.sal>
(select e.sal from emp e where e.empno='7654')and e1.job=
(select e2.job from emp e2 where e2.empno='7788');
查询出每个部门的最低工资和最低工资的对应人和部门名称
select d.dname,a.minsal,e.ename from
(select deptno,min(sal) minsal from emp group by deptno)a,emp e
where d.deptno=a.deptno and e.sal=a.min;
12.ROWNUM分页查询
ROWNUM:表示行号,实际上此行号是一个列但是这个列是一个伪列,此列可以在每张表出现
查询emp表中带有rownum列
select rownum,t.* from emp t;
--就会在该行上加上一个行号行号从1开始,一次递增,不能跳着走
---emp表工资倒叙排列后,每页五条记录,查询第二页
--排序操作会影响rownum的顺序
select rownum,emp.* from emp order by emp.sal desc;
--如果涉及到排序,但是还要使用rownum的话我们可以再次嵌套循环
select rownum,t.* from(
select rownum,emp.* from emp order by emp.sal desc)t;
--emp表工资倒叙排列后,每页五条记录,查询第二页
--rownum行号不能写上一个大于正数
select * from(
select rownum rn,e.* from(
select * from emp order by sal desc)e
where rownum<11)
where rn>5
第一种写法:
select * from (select rownum rm,e.* from (select * from emp)e where rownum<11)b where b.rm>5;
第二种写法:
select * from(select rownum r,emp.* from emp )b where b.r>5 and
b.r<11;
13.视图
1>视图就是封装了一条复杂查询的语句
1).语法:CREATE VIEW 视图名称 AS 子查询
建立视图,视图中包含了编号为20的所有员工信息
create view empvd20 as select * from emp t where t.deptno=20;
视图创建完成可以使用视图来查询视图中所有的信息
select * from empvd20 t;
2).语法:CREATE OR REPLACE VIEW 视图名称 AS 子查询
如果视图已经存在我们可以使用语法2来创建视图,这样已有的视图就会被全部的覆盖
create or replace view empvd20 as select * from emp t where t.deptno=20;
3).我们也可以通过修改视图来修改原来的数据但是我们并不希望这样做一般的视图都会设置为只读属性
语法:CRETAE OR REPLACE VIEW 视图名称 AS 子查询 WITH READ ONLY
create or replace view empvd20 as seelct * from emp t where t.deptno=20 with reda only;
14.PL/SQL基本语法
什么是PL/SQL?
PLSQL是Oracle对sql语言的过程化扩展,指在SQL命令语言中增加了过程处理语句(如分支,循环等)使SQL语言具有过程处理能力把SQL语言的操纵能力与过程语言的数据处理能力结合起来使得PLSQL面向过程但比过程语言简单,高效,灵活和实用
范例1:为职工涨工资,每个人涨10%的工资
update emp seet sa=sal*1.1;
1>PL/SQL程序语法
程序语法:
declare
说明部分(变量说明,游标,例外说明)
begin
语句序列(DML语句)
exception
例外处理语句
End;
2>常量和变量定义
在程序的声明的阶段可以来定义常量和变量
1).变量的基本类型就是Oracle中的建表时字段的变量如:char,varchar2,date,number,boolean,long
定义语法:var1 char(15);
Psal number(9,2);
说明变量名,数据类型和长度后用分号结束说明语句
常量定义:married constant boolean:=true;
2).引用变量
Myname emp.ename%type;
引用型变量,即my_name的类型与emp表中的ename列的类型一样在Sql中使用into来赋值
declare
emprec emp.ename%type;
begin
select t.ename into emprec from emp t where t.empno=7369;
dbms_output.put_line(emprec);
3).记录型变量
Emprec emp%rowtype
记录变量分量的引用
emp_rec.ename:='ADAMS';
declare
p emp%rowtype;
begin
select * into p from emp t where t.empno = 7369; dbms_output.put_line(p.ename || ' ' || p.sal);
end;
3>if分支
语法 1:IF 条件 THEN 语句 1;
语句 2;
END IF;
语法 2:IF 条件 THEN 语句序列 1;
ELSE 语句序列 2;
END IF;
语法 3:IF 条件 THEN 语句;
ELSIF 语句 THEN 语句;
ELSE 语句;
END IF;
范例 1:如果从控制台输入 1 则输出我是 1
declare
pnum number := #
begin
if pnum = 1 then
dbms_output.put_line('我是1');
end if;
end;
范例 2:如果从控制台输入 1 则输出我是 1否则输出我不是 1
declare
mynum number := #
begin
if mynum = 1 then
dbms_output.put_line('我是1');
else
dbms_output.put_line('我不是1');
end if;
end;
范例 3:判断人的不同年龄段 18岁以下是未成年人,18岁以上 40以下是成年人,40以上是老年人
declare
mynum number := #
begin
if mynum < 18 then
dbms_output.put_line('未成年人');
elsif mynum >= 18 and mynum < 40 then
dbms_output.put_line('中年人');
elsif mynum >= 40 then
dbms_output.put_line('老年人');
end if;
end;
4>LOOP循环语句
语法1:WHILE total<=25000 LOOP
....
total=total+salary;
END LOOP;
语法2:LOOP
EXIT[when 条件];
....
END LOOP;
语法3:FOR 1 IN 1..3 LOOP
END LOOP
范例:使用语法 1 输出 1 到10 的数字
declare
step number := 1;
begin
while step <= 10 loop
dbms_output.put_line(step);
step := step + 1;
end loop;
end;
范例:使用语法 2 输出 1 到10 的数字
declare
step number := 1;
begin
loop
exit when step > 10;
dbms_output.put_line(step);
step := step + 1;
end loop; end;
范例:使用语法 3 输出 1 到10 的数字
declare
step number := 1;
begin
for step in 1 .. 10
loop dbms_output.put_line(step);
end loop;
end;
5>游标Cursor
在写java程序中有集合的概念,那么在pl/sql中也会用到多条记录,这个时候我们就要用到游标,游标可以存储查询返回的多条数据
语法:CURSOR 游标名[(参数名 数据类型,参数名 数据类型,...)]IS SELECT 语句;
如:cursor c1 is select ename from emp;
1).游标的使用步骤
1.打开游标:open cl;(打开游标执行查询)
2.取一行游标的值:fetch into pjob;(取一行到变量中)
3.关闭游标:close c1;(关闭游标释放资源)
4.游标的结束方式:exit when c1%notfound
5.注意上面的pjob必须与emp表中的job类型一致
范例1:使用游标的方式输出emp表中员工的姓名和编号
declare
cursor pc is select * from emp;
pemp emp% rowtype;
begin
open oc;
loop
fetch pc
into pemp;
exit when pc%notfound;
dbms_output.put_line(pemp.empno||pemp.ename);
end loop;
close pc;
end;
范例 2:按员工的工种涨工资,总裁 1000 元,经理涨 800 元其,他人员涨 400 元。 备份出一张新表为 myemp;
create table myemp as select * from emp;
declare
cursor pc is select * from myemp;
addsal myemp.sal%type;
pemp myemp%rowtype;
begin
open pc;
loop
fetch pc
into pemp;
exit when pc%notfound;
if pemp.job = 'PRESIDENT' then
addsal := 1000;
elsif pemp.job = 'MANAGER' then
addsal := 800;
else
addsal := 400;
end if;
update myemp t set t.sal = t.sal + addsal where t.empno = pemp.empno;
end loop;
close pc;
end;
范例 3:写一段PL/SQL 程序,为部门号为 10 的员工涨工资。
declare
cursor pc(dno myemp.deptno%type) is select empno from myemp where deptno = dno;
pno myemp.empno%type;
begin
open pc(20);
loop
fetch pc
into pno;
exit when pc%notfound;
update myemp t set t.sal = t.sal + 1000 where t.empno = pno; end loop;
close pc;
end;
6>存储过程
存储过程是在大型数据库系统中,一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过制定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它,存储过程是数据库中的一个重要对象,任何一个设计良好的数据库应用程序应该用到存储过程
创建存储过程的语法:
CRETAE[or replace]PROCEDURE 过程名[(参数名 in/out 数据类型)]
AS
Begin
PLSQL子程序体;
End;
或者
CRETAE[or replace]PROCEDURE 过程名[(参数名 in/out数据类型)]
is
begin
PLSQL子程序体;
End过程名
范例1:创建一个输出helloworld的存储过程
create or replce procedure helloworld is
begin
dbms_output.put_line('helloworld');
end helloword;
调用存储过程
在plsql中调用存储过程
begin
--call the procedure
helloworld;
end;
范例2:给指定的员工工资100工资,并打印出涨前和涨后的工资
分析:我们需要使用带有参数的存储过程
create or replace procedure addSall(eno in number)is pemp myemp%rowtype;
begin
select * into pemp from myemp where mep=eno;
update myemp set sal=sal+100 where empno=eno;
dbms_output.put_line('涨工资前'||pemp.sal||'涨工资后'||(pemp.sal+100));
end addSall;
调用
begin
addsall(eno=>7902);
commit;
end;
7>存储函数
create or replace function 函数名(Name is type,Name in type...)retun 数据类型 is 结果变量 数据类型;
begin
return(结果变量)
end 函数名;
存储过程和存储函数的区别
一般来讲,过程和函数的区别在于函数可以有一个返回值,而过程没有返回值但过程和函数都可以通过out指定一个活多个输出参数.我们可以利用out产生,在过程和函数中实现返回多个值
范例:使用存储函数来查询指定员工的年薪
cretae or replace function empincome(eno in emp.empno%type)return number is
psal emp.sal%type;
pcomm emp.comm%type;
begin
select t.sal into psal from emp t where t.empno=eno;
return psal*12 +nvl(pcomm,0);
end;
使用存储过程来替换上面的例子
create or replace procedure empincomep(eno in emp.empno%type, income out number) is psal emp.sal%type;
pcomm emp.comm%type;
begin
select t.sal, t.comm into psal, pcomm from emp t where t.empno = eno;
income := psal*12+nvl(pcomm,0);
end empincomep;
调用:
declare
income number;
begin
empincomep(7369,income);
dbms_output.put_line(income);
end;
8>触发器
数据库触发器是一个与表相关的,存储的PL/SQL程序。每当一个特定的数据操作语句(Insert,update,delete)在指定的表上发出时,Oracle自动的执行触发器中定义的语句序列
触发器可用于:
·数据确认
·实施复杂的安全性检查
·做审计,跟踪表上所做的数据操作等
·数据的备份和同步
触发器的类型:
语句级触发器:在指定的操作语句之前或之后执行一次,不管这条语句影响多少行
行级触发器(FOR EACH ROW):触发语句作用的每一条记录都被触发在行级触发器中使用old 和new伪记录变量,识别值的状态
语法:
CREATE [or REPLACE]TRIGGER 触发器名称
{BEFORE|AFTER}
{DELETE|INSERT|UPDATE[OF列名]}
ON 表名
[FOR EACH ROW[WHEN(条件)]]
begin
PLSQL块;
END触发器名;
范例:插入员工后打印一句话"一个新员工插入"
create or replace tigger testTigger
after insert on person
declare
--local variables here
begin
dbms_output.put_line('一个新的员工被插入');
end testTrigger;
范例:不能在休息的时候插入员工
create or replace trigger validInsertPerson
before insert on person
declare
weelend varchar2(10);
begin
select to_char(sysdate,'day')into weekend from dual;
if weekend in('星期一')then
raise_application_error(-20001,'不能在非法时间插入员工');
end if;
end validInsertPerson;
在触发器中触发语句与伪记录变量的值
触发语句 :old :new
Insert 所有的字段都是null 将要插入的数据
Update 更新以前该行的值 更新以后的值
delete 删除以前该行的值 所有的字段都输null的
范例:判断员工涨工资之后的工资的值一定要大于涨工资之前的工资
create or replace trigger addsal4p
before update of sal on myemp
for each row begin
if :old.sal >= :new.sal then
raise_application_error(-20002, '涨前的工资不能大于涨后的工资');
end if;
end;
调用
update myemp t set t.sal=t.sal-1;