zoukankan      html  css  js  c++  java
  • 笔记11:Oracle基础

    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 数据类型...]
    AS 或 is
    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();
    	}
    }
    
  • 相关阅读:
    hadoop 配置
    spark 学习网站和资料
    spark-submit 提交任务及参数说明
    python 浮点运算
    nginx 和 php
    clojure 语法
    编程语言
    spark
    mvn 与 pom.xml
    偏导数与偏微分
  • 原文地址:https://www.cnblogs.com/xiaoaiying/p/13344656.html
Copyright © 2011-2022 走看看