Oracle基础

(184) 2024-03-20 19:01:01

Oracle基础

单行函数:

作用于一行,返回一个值

  • 字符函数
select upper("yse") from dual;--结果:YES
select lower("YES") from dual;--结果:yse
  • 数值函数
select round(26.18,1) from dual; --四舍五入,后面参数表示保留的小数, 整数==往后保留,负数==往前保留
select trunc(56.16,1) from dual; --直接截取,不再看后面位数是否大于5
select mod(10,3) from dual; --求模(取余) 结果:1
  • 日期函数
--查询出emp表中所有员工入职距离现在几天
select sysdate-e.hiredate 
from emp e;--sysdate==当前系统时间

--算出明天此刻
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 round((sysdate-e.hiredate)/7) 
from emp e;round()==四舍五入函数
  • 转换函数
--时间转字符串格式
select to_char(sysdate,'fm yyyy-mm-dd hh24:mi:ss') 
from dual;  fm==时分秒前不加0 24==24小时制

--字符串转日期
select to_char('2020-7-13 22:41:21','fm yyyy-mm-dd hh24:mi:ss') 
from dual;
  • 通用函数
-- 算出emp表中所有员工的年薪
--sal=月薪字段 e=emp的别名 comm=一年的奖金
select e.sal*12+e.comm
from emp e; 

--奖金里面有null时,null值和任意数字做算数运算,结果都是null
--解决:nvl()函数:如果值为null用0代替
select e.sal*12+nvl(e.comm,0)
from emp e;

多行函数

作用于多行,返回一个值

方法
count(1) 查询总数量,
1= * 表示主键即所有
sum(‘字段’) 求和
max(‘字段’) 最大值
min(‘字段’) 最小值
avg(‘字段’) 平均值

分组查询

-- 查询出每个部门的平均工资
select e.deptno, avg(e.sal) --如果加这段:,e.ename 则报错
from emp e 
group by e.deptno
  • 分组查询中,出现在group by后面的原始列,才能出现在select后面
  • 没有出现在group by后的列,想在select后面出现,必须加上聚合函数
  • 聚合函数有一个特性,可以把多行记录变成一个值
--查询出平均工资高于2000的部门信息
select e.deptno, avg(e.sal) asal  --如果加这段及haveing后面用asal来判断则报错
from emp e
group by e.deptno
having avg(e.sal)>2000;
  • 所有条件都不能使用别名来判断。

如:select ename,sal s from emp where s>1500; --会报错,s别名不能用来判断

where和having的区别

  • where是过滤分组前的数据,having是过滤后的数据
  • 表现形式:where 必须是在group by之前,having是在group by之后

子查询

子查询返回一个值

-- 查询工资和员工SCOTT一样的员工信息
select * 
from emp 
where sal = (-- 由于ename字段不是主键,姓名为'SCOTT'的数据可能不止一条,所以上面用等号有时会有问题 解决:用in代替等号即可
    select sal 
    from emp 
    where ename = 'SCOTT';
)

子查询返回一个集合

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

子查询返回一张表

-- 查询出每个部门最低工资,和最低工资员工姓名,和该员工所在部门名称
-- 三表联查,得到最终结果
select t.deptno ,t.msal,e.ename,d.dname
from(
    select deptno min(sal) msal --1-先查询每个部门最低工资 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;

oracle分页

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

-- 例:emp表工资倒叙排序后,每页五条记录,查询第二页
-- 第二页即:行号为6-10

-- 说明:排序操作会影响rownum的顺序 如:
select * 
from emp e 
where rownum < 4 
order by e.sal desc; --查询排序后的前三条

解决:	
select rownum,t.* 
from(
    select * 
	from emp e 
	order by e.sal desc
) t 
where rownum<4;  -- 先排序再加行号


-- 说明:rownum行号不能使用大于>
select rownum,e.* 
from(select * 
	 from emp e 
	 order by e.sal desc
) 
where rownum<11 and rownum>5 -- 查询无数据
		

-- 正确分页查询语句:
select *
from(
	select rownum rn,tt.*
	from(
		select *
		from emp
		order by sal desc
    ) tt
	order by e.sal desc
	where rownum<11
)
where rn>5;

视图

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

视图的作用:

  • 视图可以屏蔽掉一些敏感字段。
  • 保证总部和分部数据及时统一。
--查询语句创建表
create table emp 
as 
select * 
from scott.emp; --scott==用户名

-- 查询
select * 
from emp;


--创建视图[必须有dba权限]
create view v_emp 
as 
select ename,job 
from emp;

--查询视图
select * from v_emp;

--修改视图(不推荐)
update v_emp 
set job='CLERK'
where ename='ALLEN';--原表数据也被修改
commit;

--创建只读视图
create view v_emp1 
as 
select ename,job 
from emp with read only;--无法修改

索引

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

单列索引

-- 创建单列索引
create index idx_ename on emp(ename);

-- 触发规则:条件必须是索引列中的原始值。
-- 单行函数,模糊查询 都会影响索引的触发
select * from emp where ename='SCOTT';

复合索引

--创建复合索引
create index idx_enamejob on emp(ename,job);
		
--触发规则:复合索引中第一列为优先检索列。如果要触发复合索引,必须包含有优先检索列中的原始值
select * from emp where ename='SCOTT' and job='xxx'; --会触发复合索引,有优先检索列ename='SCOTT'
select * from emp where ename='SCOTT' or job='xxx';  --不触发索引。相当于两个语句,一个触发一个不触发即不触发。
select * from emp where ename='SCOTT'; 				 --触发的是单列索引,既有单列也有复合,但查询为单列

pl/sql编程语言

  • 是对sql语言的扩展,是sql语言具有过程化编程的特性。
  • 比一般的过程化编程语言更灵活高效。
  • 主要用来编写存储过程和存储函数等。

声明方法:

declare
	--定义一个数字变量i赋值为10
	i number(2) := 10; 
	
	--定义字符串变量s赋值为小明
	s varchar2(10) := '小明'; 
	
	--引用型变量,定义一个emp表中ename字段类型的变量ena
	ena emp.ename%type; 
	
	--记录型变量,定义一个存emp表中一行记录的变量,相当于一个对象
	emprow emp%rowtype; 
begin
	
	dbms_output.put_line(i); --输出变量i 结果:10
	dbms_output.put_line(s); --输出变量s 结果:小明
	
	select ename into ena 
	from emp 
	where empno=7788; --从emp表中查询一个姓名赋值给变量ena
	dbms_output.put_line(ena); --输出变量ena
	
	select * into emprow 
	from emp 
	where empno=7788;
	dbms_output.put_line(emprow.ename ||'的工作为:'|| emprow.job); --结果例如==SCOTT的工作为:ANALYST
	
end;

--总结:
赋值操作可以用【:=】也可以用【into查询语句】赋值

流程结构:

if判断:

--输入小于18的数字,输出未成年;输入大于18小于40数字,输出中年人;输入大于40的数字,输出老年人
declare
	i number(3) := &ii; --输入一个数值类型的数据并赋值给i
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;

loop循环:

--用三种方式输出1-10
-------------------------------------------while循环
declare
	i number(2) :=1;
begin
	while i<11 loop
		dbms_output.put_line(i);
		i := i+1;
	end loop;
end;
-------------------------------------------exit循环
declare
	i number(2) :=1;
begin
	loop
		exit when i>10;
			dbms_output.put_line(i);
			i := i+1;
	end loop;
end;
-------------------------------------------for循环
declare
	
begin
	for i in 1..10 loop
		dbms_output.put_line(i);
	end loop;
end;

游标

概念:可以存放多个对象,多行记录,相对于集合

-- 输出emp表中所有员工的姓名及工作
declare
	cursor c1 is 
	select * 
	from emp; --定义了一个游标c1,并把emp表中所有数据存入了游标
	
	emprow emp%rowtype; --记录型变量,定义一个存emp表中一行记录的变量,相当于一个对象
begin
	open c1;--打开游标
		loop
			fetch c1 into emprow; --把emp一行数据复制给变量emprow
			exit when c1%notfound; --当游标找不到数据即最后一行时退出循环
			dbms_output.put_line(emprow.ename ||'--'|| emprow.job);
		end loop;
	close c1;--关闭游标
end;
--给指定部门员工涨工资
declare
	cursor c2(eno emp.deptno%type) is 
	select empno 
	from emp 
	where deptno=eno; --定义了一个游标并需要传入一个参数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;
	cloce c2;
end;

存储过程

概念:存储过程就是提取已经编译好的一段pl/sql语言,放置在数据库端,可以直接被调用,这一段pl/sql一般都是固定步骤的业务。

--创建存储过程语法: 
create [or replace] procedure 过程名[参数名 in/out 数据类型...]
ASis
begin
	plsql子程序体;
end;

-- 例:给指定员工涨100块钱
create or replace procedure pl(eno emp.empno%type) --or replace表示如果已经存在该存储过程仍可以正常执行
is
begin
	update emp set sal = sal+100 where empno = eno;
	commit;
end;

--测试存储过程p1
declare
begin
	pl(7788); --结果:员工编号为7788的员工工资涨100
end;

out类型参数如何使用

--使用存储过程来算年薪
create or replace procedure p_yearsal(eno emp.empno%type,yearsal out number) --yearsal out number表示yearsal参数为输出参数
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); --yearsal的值在存储过程内被改变
	dbms_output.put_line(yearsal); --结果:输出员工编号为7788的员工的年薪
en

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

存储函数

与存储过程区别:

  • 存储函数可以有一个返回值,而存储过程没有
  • 存储过程和存储函数的参数都不能带长度
  • 存储函数的返回值类型不能带长度
--通过存储函数实现计算指定员工的年薪
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
	sal_year number(10);
begin
	sal_year := f_yearsal(7788); 
	dbms_output.put_line(sal_year);--结果:输出员工编号为7788的员工的年薪
end;

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

  • 语法:
    • 关键字不一样
    • 存储函数比存储过程多了两个return
  • 本质区别:
    • 存储函数有返回值,二存储过程没有返回值
    • 如果存储过程想实现有返回值的业务,我们就必须使用out类型的参数
    • 即便是存储过程使用out类型的参数,其本质也不是真的有了返回值
    • 而是在存储过程内部给out类型参数赋值,在执行完毕后,我们直接拿到输出类型参数的值。

触发器

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

  • 语句级触发器:不包含有for each row的
-- 例:插入一条记录,输出'创建xxx'

--创建一个触发器t1
create or replace trigger t1
after 		--语句执行前还是后执行该触发器
insert		--执行什么语句会触发
on person	--作用哪个表的触发器
declare
begin
  dbms_output.put_line('创建了一个person');
end;

--触发t1
insert into person(pid, name, gender, birthday) values(2, '李四', 1, to_date('2020-7-17', 'yyyy-MM-dd'));
  • 行级触发器:

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

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

-- 例:不能给员工降薪
create or replace trigger t2
before
updater
on emp
for each row --表示行级触发器
declare

begin
	if :old.sal > :new.sal then
		-- 抛出一个异常raise_application_error(-20001~-20999之间,'错误提示信息');
		raise_application_error(-20001,'不能给员工降薪');
	end if;
end;

-- 例:使用触发器实现主键自增
--分析:在用户插入操作之前,拿到即将插入的数据,给该数据中的主键列赋值,使用该功能用到行级触发器
create or replace trigger auid
before
insert
on person
for each row
declare

begin
	select s_id_person.nextval into :new.pid from dual;
	--s_person 自定义序列号名字 
	--nextval 取序列的下一个值 
	--dual 是oracle的bai虚拟表du,不是真实存在的
end;

java调用存储过程或存储函数

--需要jar包
	oracle10g == ojdbc14.jar
	oracle11g == ojdbc6.jar
--需要接口
	CallableStatement
public class OracleDemo { 
   
	//测试连接oracl数据库
	@Test
	public void javaCallOracle() throws Exception { 
   
		//加载数据库驱动
		Class.forName("oracle.jdbc.driver.OracleDriver");
		//得到Connection连接
		Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@192.168.88.6:1521:orcl",
				"root", "root");
		//得到预编译的Statement对象
		PreparedStatement pstm = connection.prepareStatement("select * from emp where empno = ?");
		//给参数赋值
		pstm.setObject(1, 7788);
		//执行数据库查询操作
		ResultSet rs = pstm.executeQuery();
		//输出结果
		while(rs.next()){ 
   
			System.out.println(rs.getString("ename"));
		}
		//释放资源
		rs.close();
		pstm.close();
		connection.close();
	}
    
	/** * java调用存储过程 * {?= call <procedure-name>[(<arg1>,<arg2>, ...)]} 调用存储函数使用 * {call <procedure-name>[(<arg1>,<arg2>, ...)]} 调用存储过程使用 * @throws Exception */
	@Test
	public void javaCallProcedure() throws Exception { 
   
		//加载数据库驱动
		Class.forName("oracle.jdbc.driver.OracleDriver");
		//得到Connection连接
		Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@192.168.88.6:1521:orcl",
				"root", "root");
		//得到预编译的Statement对象
		CallableStatement pstm = connection.prepareCall("{call p_yearsal(?, ?)}");//call p_yearsal(?, ?)为存储过程,带两个参数
		//给参数赋值
		pstm.setObject(1, 7788);//第一个参数,查询的id
		pstm.registerOutParameter(2, OracleTypes.NUMBER);//无返回值,第二参数表示输出类型的参数==员工年薪
		//执行数据库查询操作
		pstm.execute();
		//输出结果[第二个参数]
		System.out.println(pstm.getObject(2));
		//释放资源
		pstm.close();
		connection.close();
	}
    
	/** * java调用存储函数 * {?= call <procedure-name>[(<arg1>,<arg2>, ...)]} 调用存储函数使用 * {call <procedure-name>[(<arg1>,<arg2>, ...)]} 调用存储过程使用 * @throws Exception */
	@Test
	public void javaCallFunction() throws Exception { 
   
		//加载数据库驱动
		Class.forName("oracle.jdbc.driver.OracleDriver");
		//得到Connection连接
		Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@192.168.88.6:1521:orcl",
				"root", "root");
		//得到预编译的Statement对象
		CallableStatement pstm = connection.prepareCall("{?= call f_yearsal(?)}");
		//给参数赋值
		pstm.setObject(2, 7788);  //第二个?表示存储函数参数==查询的id
		pstm.registerOutParameter(1, OracleTypes.NUMBER);//第一?表示存储函数返回值
		//执行数据库查询操作
		pstm.execute();
		//输出结果[第一个参数]
		System.out.println(pstm.getObject(1));
		//释放资源
		pstm.close();
		connection.close();
	}
}
THE END

发表回复