2019独角兽企业重金招聘Python工程师标准>>>
一:数据库分类
小:access/foxbase等
中:mysql/sql server/informix等
大:oracle/sybase/db2
二:sqlpus常用命令
oracle自带用户:sys(超级用户,具有sysdba角色,可以创建数据库)、system(具有sysoper角色),区别在于是否能创建数据库。
1:连接命令:【conn 用户名/密码@网络服务名 [as sysdba/sysoper]】
2:显示当前用户:【show user】
3:断开连接:【disc/disconn/disconnect】
4:修改当前用户的密码:【passw/password】,修改其他用户密码(权限):【alter user 用户名 identity by 新密码】
5:退出sqlplus:【exit】
6:清空屏幕:【clear screen】
7:运行sql脚本:【start/@ url.sql】
8:编辑sql脚本:【edit url.sql】
9:将屏幕内容输入到指定文件:【spool url.sql】【spool off】
10:交互式命令&:【&value】需要用户输入值
11:显示和设置环境变量:【show/set linesize/pagesize】
12:创建用户(dba权限):create user 用户名 identified by 密码;
13:删除用户:【drop user 用户名 [cascade]】
14:授予权限(系统和对象权限):【grant 权限 to 用户 [with grant option]/[with admin opyion]】
15:收回权限:【revoke 权限 on 对象 from 用户】(系统权限不级联,对象权限级联收回)
16:账号锁定(创建profile口令限制的集合):【create profile lock_account limit failed_login_attempts 3 password_lock_time 2;】【alter user scott profile lock_account;】
17:账户解锁:【alter user scott account unlock;】
18:终止口令(使用户定期修改密码):【create profile myprofile limit password_life_time 10 password_grace_time 2;】【alter user test profile myprofile;】
19:口令历史:【create profile password_history limit password_life_time 10 password_grace_time 2 password_reuse_time 10】【alter user test profile password_history;】
20:删除profile:【drop profile password_history [casade]】
三:常用SQL命令
1:数据类型:char/varchar2(n)/varchar/number(n,m)/date/timestamp/blob
2:创建表:create table demo( filed1 varchar2(20),...);
3:修改表:alter table demo add/modify ( filed1 varchar2(20))/drop column field1;
4:修改表名字:rename demo to test;
5:删除表:drop table demo;
6:添加数据:insert into table demo values(...);
7:修改数据:update demo set ... where ...
8:删除数据:delete from demo where ...;
9:删除表结构和数据:drop table demo;
10:删除所有表记录,无日志写入无法找回删除的记录:truncate table demo;
11:查看表结构:desc demo;
12:查询所有列:select * from demo;
13:底部显示操作时间:set timing on/off;
14:表复制语句:insert into t2(filed1,...) select v1,v2,...from t1;
15:查询统计:select count(*) from demo;
16:查询语句:select [distinct] field1 [as alias],... from ... where ...
17:nvl函数:nvl(str1,replace_str2)
18:连接字符串:||
19:模糊查询:like '%demo_';
20:范围查询:in (A,B,C);
21:判空查询:is null/null
22:升降序排列:order by [asc/desc]
23:聚合函数(如果列里面有一个分组函数,其它都必须是分组函数):max/min/avg/sum/count
24:分组统计:group by ... having ... (order by ...)
25: 当在from 子句中使用子查询时,必须给子查询指定别名 , 给表取别名的时候,不能加as;但是给列取别名,是可以加as的。
26:根据查询结果创建新表: CREATE TABLE mytable (id, name, sal, job, deptno) as SELECT empno, ename, sal, job, deptno FROM emp;
27:合并查询:union(去掉重复行)/union all/intersect/minus
四:oracel分页方式
1:rowid分页
SELECT *
FROM EMP
WHERE ROWID IN
(SELECT RID
FROM (SELECT ROWNUM RN, RID
FROM (SELECT ROWID RID, EMPNO FROM EMP ORDER BY EMPNO DESC)
WHERE ROWNUM <= ( (currentPage-1) * pageSize + pageSize )) --每页显示几条
WHERE RN > ((currentPage-1) * pageSize) ) --当前页数
ORDER BY EMPNO DESC;
2:rownum分页
SELECT *
FROM (SELECT T.*, ROWNUM RN
FROM (SELECT * FROM EMP ORDER BY EMPNO DESC) T
WHERE ROWNUM <= ( (currentPage-1) * pageSize + pageSize )) --每页显示几条
WHERE RN > ( (currentPage-1) * pageSize ); --当前页数
3:分析函数分页
SELECT *
FROM (SELECT T.*, ROW_NUMBER() OVER(ORDER BY empno DESC) RK FROM emp T)
WHERE RK <= ( (currentPage-1) * pageSize + pageSize ) --每页显示几条
AND RK > ( (currentPage-1) * pageSize ); --当前页数
解析步骤:
1:SELECT * FROM emp;
2:显示rownum,由oracle分配的
SELECT e.*, ROWNUM rn FROM (SELECT * FROM emp) e; --rn相当于Oracle分配的行的ID号
3:先查出1-10条记录
正确的: SELECT e.*, ROWNUM rn FROM (SELECT * FROM emp) e WHERE ROWNUM<=10;
错误的:SELECT e.*, ROWNUM rn FROM (SELECT * FROM emp) e WHERE rn<=10;
4:然后查出6-10条记录
SELECT * FROM (SELECT e.*, ROWNUM rn FROM (SELECT * FROM emp) e WHERE ROWNUM<=10) WHERE rn>=6;
五:事务操作
1:设置保存点:savepoint a;
2:回滚到保存点:rollback to a;
3:回滚全部事务操作:rollback;
4:提交事务:commit;
5:java中使用事务:conn.setAutoCommit(false);关闭自动提交事务,然后事务机制操作
6:只读事务: set transaction read only;
六:常用函数
1:字符函数: lower(char) /upper(char)/length(char)/substr(char, m, n)/replace(char1, search_string, replace_string) /instr(C1,C2,I,J)
2:数字函数: cos,cosh,exp,ln, log,sin,sinh,sqrt,tan,tanh,acos,asin,atan,round 等
3:日期函数: sysdate (返回系统时间 )、 add_months(time,moths)函数 ( 得到某一时间之前或之后n个月的时间 )、 last_day(d)(返回指定日期所在月份的最后一天 )
4:转换函数: to_char(date,formatStr)函数(
yy:两位数字的年份 2004-->04
yyyy:四位数字的年份 2004年
mm:两位数字的月份 8 月-->08
dd:两位数字的天 30 号-->30
hh24: 8点-->20
hh12:8点-->08
mi、ss-->显示分钟\秒
9:显示数字,并忽略前面0
0:显示数字,如位数不足,则用0补齐
.:在指定位置显示小数点
,:在指定位置显示逗号
$:在数字前加美元
L:在数字前面加本地货币符号
C:在数字前面加国际货币符号
G:在指定位置显示组分隔符、
D:在指定位置显示小数点符号(.)
)
to_date()函数 ('dateStr',formatStr)
5:sys_context(‘USERENV’,param)系统函数:(param=teminal/language/db_name/nls_date_format/session_user/currentt_schema/host)
6:管理员区别: sysdba>sysoper>dba
7:显示初始化参数:show parameter
8:修改参数: oracle\admin\myoral\pfile\init.ora 文件中去修改。
七:备份与恢复
一:备份(导出):导出表、导出方案、导出数据库
参数:
userid:用于指定执行导出操作的用户名,口令,连接字符串
tables:用于指定执行导出操作的表
owner:用于指定执行导出操作的方案
full=y:用于指定执行导出操作的数据库
inctype:用于指定执行导出操作的增量类型
rows:用于指定执行导出操作是否要导出表中的数据
file:用于指定导出文件名
1:导出表:
exp userid=scott/oracle@orcl tables=(emp) file=d:\emp.dmp --导出单个表
exp userid=scott/oracle@orcl tables=(emp,dept) file=d:\emp.dmp --导出多个表
导出其他方案的表:
exp userid=system/oracle@orcl tables=(scott.emp) file=d:\emp.emp
exp userid=system/oracle@orcl tables=(scott.emp,scott.dept) file=d:\emp.emp
导出表结构:
exp userid=scott/oracle@orcl tables=(emp) file=d:\emp.dmp rows=n
直接导出方式 :
exp userid=scott/oracle@orcl tables=(emp) file=d:\emp.dmp direct=y
2:导出方案:
导出方案:
exp userid=scott/oracle@orcl owner=scott file=d:\scott.dmp
导出其它方案:
exp userid=system/oracle@orcl owner=(system,scott) file=d:\system.dmp
3:导出数据库
exp userid=system/oracle@orcl full=y inctype=complete file=d:\all.dmp
二:恢复(导入)
参数:
userid:用于指定执行导入操作的用户名,口令,连接字符串
tables:用于指定执行导入操作的表
formuser:用于指定源用户
touser:用于指定目标用户
file 用于指定导入文件名
full=y:用于指定执行导入整个文件
inctype:用于指定执行导入操作的增量类型
rows:指定是否要导入表行(数据)
ignore:如果表存在,则只导入数据
1:导入表
导入表:
imp userid=scott/oracle@orcl tables=(emp) file=d:\xx.dmp
导入表到其它用户:
imp userid=system/oracle@orcl tables=(emp) file=d:\xx.dmp touser=scott
导入表结构:
imp userid=scott/oracle@orcl tables=(emp) file=d:\xx.dmp rows=n
导入数据:
imp userid=scott/oracle@orcl tables=(emp) file=d:\xx.dmp ignore=y
2:导入方案
导入自身的方案:
imp userid=scott/oracle@orcl file=d:\xxx.dmp
导入其它方案:
imp userid=system/oracle@orcl file=d:\xxx.dmp fromuser=system touser=scott
3:导入数据库(相当于数据库迁移)
imp userid=system/oracle@orcl full=y file=d:\xxx.dmp
八:视图操作
1:数据字典视图: user_xxx,all_xxx,dba_xxx
user_tables: 用于显示当前用户所拥有的所有表,它只返回用户所对应方案的所有表 。
all_tables: 用于显示当前用户可以访问的所有表,它不仅会返回当前用户方案的所有表,还会返回当前用户可以访问的其它方案的表 。
dba_tables: 它会显示所有方案拥有的数据库表。但是查询这种数据库字典视图,要求用户必须是dba角色或是有select any table 系统权限。
2:用户名和权限和角色
dba_users可以显示所有数据库用户的详细信息;
dba_sys_privs可以显示用户所具有的系统权限;
dba_tab_privs可以显示用户具有的对象权限;
dba_col_privs 可以显示用户具有的列权限;
dba_role_privs 可以显示用户所具有的角色。
九:表空间和数据文件
表空间:数据库的逻辑组成部分, 物理上讲,数据库数据存放在数据文件中。逻辑上讲,数据库数据则是存放在表空间中,表空间由一个或多个数据文件组成( 数据库由表空间构成,而表空间又是由段构成,而段又是由区构成,而区又是由oracle块构成 )。
1:建立表空间: create tablespace data01 datafile 'D:\dev\oracle\product\10.2.0\dada01.dbf' size 20m uniform size 128k;
2:使用表空间:
create table mypart(
deptno number(4),
dname varchar2(14),
loc varchar2(13)
) tablespace data01;
3:改变表空间状态:
表空间脱机: alter tablespace 表空间名 offline;
表空间联机: alter tablespace 表空间名 online;
只读表空间: alter tablespace 表空间名 read only;
4:删除表空间: drop tablespace ‘表空间’ including contents and datafiles;
5:扩展表空间:
增加数据文件 : alter tablespace sp01 add datafile 'D:\dev\oracle\product\10.2.0\dada02.dbf' size 1m;
修改数据文件的大小 : alter tablespace sp01 'D:\dev\oracle\product\10.2.0\dada01.dbf' resize 4m;
设置文件的自动增长 : alter tablespace sp01 'D:\dev\oracle\product\10.2.0\dada01.dbf' autoextend on next 10m maxsize 500m;
6:移动数据文件: 确定数据文件所在的表空间 》 使表空间脱机 》 使用命令移动数据文件到指定的目标位置 》 执行alter tablespace 命令 》 使得表空间联机
select tablespace_name from dba_data_files where file_name=upper('D:\dev\oracle\product\10.2.0\dada01.dbf');
alter tablespace sp01 offline;
host move D:\dev\oracle\product\10.2.0\dada01.dbf c:\dada01.dbf;
alter tablespace sp01 rename datafile 'D:\dev\oracle\product\10.2.0\dada01.dbf' to 'c:\sp01.dbf';
alter tablespace sp01 online;
7:显示表空间信息: select tablespace_name from dba_tablespaces;
8: 除了最常用的数据表空间外,还有其它类型表空间:索引表空间、 undo表空间、 临时表空间、 非标准块的表空间。
十:约束
1:约束包括:not null、 unique, primary key, foreign key和check 五种。
1)、not null(非空)
如果在列上定义了not null,那么当插入数据时,必须为列提供数据。
2)、unique(唯一)
当定义了唯一约束后,该列值是不能重复的,但是可以为null。
3)、primary key(主键)
用于唯一的标示表行的数据,当定义主键约束后,该列不但不能重复而且不能为null。
需要说明的是:一张表最多只能有一个主键,但是可以有多个unqiue约束。
4)、foreign key(外键)
用于定义主表和从表之间的关系。外键约束要定义在从表上,主表则必须具有主键约束或是unique 约束,当定义外键约束后,要求外键列数据必须在主表的主键列存在或是为null。
5)、check
用于强制行数据必须满足的条件,假定在sal列上定义了check约束,并要求sal列值在1000-2000之间如果不在1000-2000之间就会提示出错。
2:删除约束
alter table 表名 drop constraint 约束名称 ;
3:显示约束信息
select constraint_name, constraint_type, status, validated from user_constraints where table_name = '表名';
select column_name, position from user_cons_columns where constraint_name = '约束名';
直接用pl/sql developer查看 .
十一:索引
1:创建索引
单列索引:单列索引是基于单个列所建立的索引
语法:create index 索引名 on 表名(列名);
复合索引:复合索引是基于两列或是多列的索引。在同一张表上可以有多个索引,但是要求列的组合必须不同,比如:create index emp_idx1 on emp(ename, job); create index emp_idx1 on emp(job, ename); 这两个索引是两个不同的索引。
2:使用原则
1)、在大表上建立索引才有意义
2)、在where子句或是连接条件上经常引用的列上建立索引
3)、索引的层次不要超过4层
3:索引缺点
1)、建立索引,系统要占用大约为表1.2倍的硬盘和内存空间来保存索引。
2)、更新数据的时候,系统必须要有额外的时间来同时对索引进行更新,以维持数据和索引的一致性。
比如在如下字段建立索引应该是不恰当的:
1. 很少或从不引用的字段;
2. 逻辑型的字段,如男或女(是或否)等。
4:索引分类
按照数据存储方式,可以分为B*树、反向索引、位图索引;
按照索引列的个数分类,可以分为单列索引、复合索引;
按照索引列值的唯一性,可以分为唯一索引和非唯一索引。
此外还有函数索引,全局索引,分区索引...
5:显示索引信息
1):在同一张表上可以有多个索引,通过查询数据字典视图dba_indexs和user_indexs,可以显示索引信息。其中dba_indexs用于显示数据库所有的索引信息,而user_indexs用于显示当前用户的索引信息:select index_name, index_type from user_indexes where table_name = '表名';
2):显示索引列:通过查询数据字典视图user_ind_columns,可以显示索引对应的列的信息
select table_name, column_name from user_ind_columns where index_name ='IND_ENAME';
3):你也可以通过pl/sql developer工具查看索引信息 。
十二:权限
一:系统权限
常用:
create session 连接数据库
create table 建表
create view 建视图
create public synonym 建同义词
create procedure 建过程、函数、包
create trigger 建触发器
create cluster 建簇
1:显示系统权限: select * from system_privilege_map order by name;
2:授予系统权限: grant create session, create table to ken with admin option;
3:回收系统权限(不级联): revoke create session from ken;
二:对象权限
常用:
insert 添加
delete 删除
alter 修改
select 查询
index 索引
references 引用
execute 执行
1:显示对象权限: select distinct privilege from dba_tab_privs;
2:授予对象权限: grant select on emp to blake with grant option;
3:授予列权限: grant select on emp(ename,sal) to monkey ;
4:回收对象权限(级联): revoke select on emp from blake ;
十三:角色
一:预定义角色: connect、resource、dba
1:connect角色
connect角色具有一般应用开发人员需要的大部分权限。
create cluster
create database link
create session
alter session
create table
create view
create sequence
2:resource角色
resource角色具有应用开发人员所需要的其它权限。
resource角色包含以下系统权限:
create cluster
create indextype
create table
create sequence
create type
create procedure
create trigger
3:dba角色:具有所有的系统权限,及with admin option选项,默认的dba用户为sys和system,它们可以将任何系统权限授予其他用户。但是要注意的是dba角色不具备sysdba和sysoper的特权(启动和关闭数据库)。
二:自定义角色
1:建立角色: create role 角色名 not identified; create role 角色名 identified by 密码;
2:角色授权: grant insert, update, delete on scott.emp to 角色名;
3:分配角色给用户: grant 角色名 to 用户 with admin option;
4:删除角色: drop role 角色名;
5:显示角色信息:
1)、显示所有角色: select * from dba_roles;
2)、显示角色具有的系统权限:select privilege, admin_option from role_sys_privs where role='角色名';
3)、显示角色具有的对象权限:通过查询数据字典视图dba_tab_privs可以查看角色具有的对象权限或是列的权限。
4)、显示用户具有的角色,及默认角色:当以用户的身份连接到数据库时,oracle 会自动的激活默认的角色,通过查询数据字典视图dba_role_privs 可以显示某个用户具有的所有角色及当前默认的角色。
select granted_role, default_role from dba_role_privs where grantee = ‘用户名’;
十四:PL/SQL
1:编码规范
2: pl/sql块: 三个部分构成:定义部分,执行部分,例外处理部分。
declare
/*定义部分——定义常量、变量、游标、例外、复杂数据类型*/
begin
/*执行部分——要执行的pl/sql 语句和sql 语句*/
exception
/*例外处理部分——处理运行的各种错误*/
end;
eg:
set serveroutput on; DECLARE --定义字符串变量 v_ename varchar2(10); v_sal NUMBER(7,2); BEGIN --执行部分 select ename, sal into v_ename, v_sal from emp where empno=&empno; dbms_output.put_line('雇员名:'||v_ename||',薪水:'||v_sal); EXCEPTION --异常处理 WHEN no_data_found THEN dbms_output.put_line('朋友,您的编号输入有误!'); end; /
3:存储过程: 用于执行特定的操作,当建立存储过程时,既可以指定输入参数(in),也可以指定输出参数(out),通过在过程中使用输入参数,可以将数据传递到执行部分;通过使用输出参数,可以将执行部分的数据传递到应用环境。在sqlplus中可以使用create procedure命令来建立过程。
编写存储过程:
CREATE PROCEDURE sp_update(uname VARCHAR2, newsal NUMBER) IS BEGIN update emp set sal=newsal where ename=uname; END; /
调用存储过程:
SQL> exec sp_update('zhangsan', 888); SQL> commit;
java中调用存储过程:
package junit.test; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; /** * 演示java程序调用oracle的存储过程案例 * * @author jiqinlin * */ public class ProcedureTest { public static void main(String[] args) { try { // 1.加载驱动 Class.forName("oracle.jdbc.driver.OracleDriver"); // 2.得到连接 Connection ct = DriverManager.getConnection( "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "oracle"); // 3.创建CallableStatement CallableStatement cs = ct.prepareCall("{call sp_update(?,?)}"); // 4.给?赋值 cs.setString(1, "SMITH"); cs.setInt(2, 4444); // 5.执行 cs.execute(); // 关闭 cs.close(); ct.close(); } catch (Exception e) { e.printStackTrace(); } } }
4:函数: 用于返回特定的数据,当建立函数时,在函数头部必须包含return子句。而在函数体内必须包含return语句返回的数据。我们可以使用create function来建立函数。
编写函数:
CREATE FUNCTION annual_incomec(uname VARCHAR2) RETURN NUMBER IS annual_salazy NUMBER(7,2); BEGIN SELECT a.sal*13 INTO annual_salazy FROM emp a WHERE a.ename=uname; RETURN annual_salazy; END; /
调用函数:
SQL> var income NUMBER; SQL> call annual_incomec('SCOTT') into:income; SQL> print income;
java中调用存储过程:
package junit.test; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; /** * 演示java程序调用oracle的函数案例 * * @author jiqinlin * */ public class ProcedureTest { public static void main(String[] args) { try { // 1.加载驱动 Class.forName("oracle.jdbc.driver.OracleDriver"); // 2.得到连接 Connection ct = DriverManager.getConnection( "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "oracle"); // 3.创建PreparedStatement PreparedStatement ps = ct.prepareStatement("select annual_incomec('SCOTT') annual from dual"); // 4.执行 ResultSet rs=ps.executeQuery(); if(rs.next()){ Float annual=rs.getFloat("annual"); System.out.println(annual); } //5、关闭 rs.close(); ps.close(); ct.close(); } catch (Exception e) { e.printStackTrace(); } } }
5:包: 用于在逻辑上组合过程和函数,它由包规范和包体两部分组成, 使用create package命令来创建包 。
1:声明包:
create package sp_package is procedure update_sal(name varchar2, newsal number); function annual_income(name varchar2) return number; end;
2:创建包体:
CREATE OR REPLACE PACKAGE BODY SP_PACKAGE IS --存储过程 PROCEDURE UPDATE_SAL(NAME VARCHAR2, NEWSAL NUMBER) IS BEGIN UPDATE EMP SET SAL = NEWSAL WHERE ENAME = NAME; COMMIT; END; --函数 FUNCTION ANNUAL_INCOME(NAME VARCHAR2) RETURN NUMBER IS ANNUAL_SALARY NUMBER; BEGIN SELECT SAL * 12 + NVL(COMM, 0) INTO ANNUAL_SALARY FROM EMP WHERE ENAME = NAME; RETURN ANNUAL_SALARY; END; END; /
调用:
exec sp_package.update_sal('SCOTT', 8888); --调用函数 var income NUMBER; CALL sp_package.ANNUAL_INCOME('SCOTT') INTO:income; print income;
6:变量:
1)、标量类型(scalar)
pl/sql中定义变量和常量的语法如下:
identifier [constant] datatype [not null] [:=| default expr]
identifier: 名称
constant:指定常量。需要指定它的初始值,且其值是不能改变的
datatype:数据类型
not null:指定变量值不能为null
:= 给变量或是常量指定初始值
default 用于指定初始值
expr :指定初始值的pl/sql表达式,可以是文本值、其它变量、函数等。
使用%type类型可以避免数字或值错误( 超过了5个字符的话,就会有“ORA-06502: PL/SQL: 数字或值错误 : 字符串缓冲区太小”错误 ) : 标识符名 表名.列名%type;
2)、复合类型(composite):
pl/sql记录类似结构体:
set serveroutput on; --打开输出选项
DECLARE
--定义一个pl/sql记录类型emp_record_type,
--类型包含3个数据NAME, SALARY, TITLE。说白了,就是一个类型可以存放3个数据,主要是为了方便管理
TYPE EMP_RECORD_TYPE IS RECORD(
NAME EMP.ENAME%TYPE,
SALARY EMP.SAL%TYPE,
TITLE EMP.JOB%TYPE);
--定义了一个sp_record变量,这个变量的类型是emp_record_type
SP_RECORD EMP_RECORD_TYPE;
BEGIN
SELECT ENAME, SAL, JOB INTO SP_RECORD FROM EMP WHERE EMPNO = 7788;
DBMS_OUTPUT.PUT_LINE('员工名:' || SP_RECORD.NAME || '工资:' || SP_RECORD.SALARY);
END;
/
pl/sql表:类似数组
方法一(推荐):
set serveroutput on; --打开输出选项
DECLARE
--定义了一个pl/sql表类型sp_table_type,该类型是用于存放EMP.ENAME%TYPE
--INDEX BY VARCHAR2(20)表示下标是字符串
TYPE SP_TABLE_TYPE IS TABLE OF EMP.ENAME%TYPE INDEX BY VARCHAR2(20);
--定义了一个sp_table变量,这个变量的类型是sp_table_type
SP_TABLE SP_TABLE_TYPE;
BEGIN
SELECT ENAME, sal INTO SP_TABLE('ename'), SP_TABLE('sal') FROM EMP WHERE EMPNO = 7788;
DBMS_OUTPUT.PUT_LINE('员工名:' || SP_TABLE('ename')||'工资:'||SP_TABLE('sal'));
END;
/
方法二:
set serveroutput on; --打开输出选项
DECLARE
--定义了一个pl/sql 表类型sp_table_type,该类型是用于存放EMP.ENAME%TYPE
--index by binary_integer表示下标是整数
TYPE SP_TABLE_TYPE IS TABLE OF EMP.ENAME%TYPE INDEX BY BINARY_INTEGER; --注意binary_integer如果换为integer就会报错,知道的朋友欢迎告诉我下
--定义了一个sp_table变量,这个变量的类型是sp_table_type
SP_TABLE SP_TABLE_TYPE;
BEGIN
SELECT ENAME,sal INTO SP_TABLE(-1),SP_TABLE(-2) FROM EMP WHERE EMPNO = 7788;
DBMS_OUTPUT.PUT_LINE('员工名:' || SP_TABLE(-1)||'工资:'||SP_TABLE(-2));
END;
/
3)、参照类型(reference)
参照变量——ref cursor游标变量:
SET serveroutput ON;
DECLARE
--定义游标
TYPE sp_emp_cursor IS REF CURSOR;
--定义一个游标变量
sp sp_emp_cursor;
--定义变量
v_ename emp.ename%TYPE;
v_sal emp.sal%TYPE;
BEGIN
OPEN sp FOR SELECT e.ename, e.sal FROM emp e WHERE e.deptno=10;
--方法一 loop循环
/*
LOOP
FETCH sp INTO v_ename, v_sal;
EXIT WHEN sp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('名字:' || V_ENAME || ' 工资:' || V_SAL);
END LOOP;*/
--方法二 while循环
/*
WHILE 1=1 LOOP
FETCH sp INTO v_ename, v_sal;
EXIT WHEN sp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('名字:' || V_ENAME || ' 工资:' || V_SAL);
END LOOP;*/
--方法三 for循环
FOR cur IN (SELECT e.ename, e.sal FROM emp e WHERE e.deptno=10) LOOP
DBMS_OUTPUT.PUT_LINE('名字:' || cur.ename || ' 工资:' || cur.sal);
END LOOP;
END;
/
4)、lob(large object)
7:pl/sql控制结构
条件分支语句: if—then,if–then–else,if–then–else if–then
1)、简单的条件判断if–then
问题:编写一个过程,可以输入一个雇员名,如果该雇员的工资低于2000,就给该员工工资增加10%。
SET serveroutput ON; CREATE OR REPLACE PROCEDURE SP_PRO6(SPNAME VARCHAR2) IS --定义 V_SAL EMP.SAL%TYPE; BEGIN --执行 SELECT SAL INTO V_SAL FROM EMP WHERE ENAME = SPNAME; --判断 IF V_SAL < 2000 THEN UPDATE EMP SET SAL = SAL + SAL * 0.1 WHERE ENAME = SPNAME; COMMIT; END IF; END; / --调用存储过程 exec SP_PRO6('ALLEN');
2)、二重条件分支 if–then–else
问题:编写一个过程,可以输入一个雇员名,如果该雇员的补助不是0就在原来的基础上增加100;如果补助为0就把补助设为200;
CREATE OR REPLACE PROCEDURE SP_PRO6(SPNAME VARCHAR2) IS --定义 V_COMM EMP.COMM%TYPE; BEGIN --执行 SELECT COMM INTO V_COMM FROM EMP WHERE ENAME = SPNAME; --判断 IF V_COMM <> 0 THEN UPDATE EMP SET COMM = COMM + 100 WHERE ENAME = SPNAME; ELSE UPDATE EMP SET COMM = COMM + 200 WHERE ENAME = SPNAME; END IF; COMMIT; END; / --调用存储过程 exec SP_PRO6('ALLEN');
3)、多重条件分支 if–then–ELSIF–then
问题:编写一个过程,可以输入一个雇员编号,如果该雇员的职位是PRESIDENT就给他的工资增加1000,如果该雇员的职位是MANAGER 就给他的工资增加500,其它职位的雇员工资增加200。
CREATE OR REPLACE PROCEDURE SP_PRO6(SPNO NUMBER) IS --定义 V_JOB EMP.JOB%TYPE; BEGIN --执行 SELECT JOB INTO V_JOB FROM EMP WHERE EMPNO = SPNO; IF V_JOB = 'PRESIDENT' THEN UPDATE EMP SET SAL = SAL + 1000 WHERE EMPNO = SPNO; ELSIF V_JOB = 'MANAGER' THEN UPDATE EMP SET SAL = SAL + 500 WHERE EMPNO = SPNO; ELSE UPDATE EMP SET SAL = SAL + 200 WHERE EMPNO = SPNO; END IF; COMMIT; END; / --调用存储过程 exec SP_PRO6(7499);
循环语句-loop:
CREATE OR REPLACE PROCEDURE SP_PRO6(SPNAME VARCHAR2) IS --定义 :=表示赋值 V_NUM NUMBER := 1; BEGIN LOOP INSERT INTO USERS VALUES (V_NUM, SPNAME); --判断是否要退出循环 EXIT WHEN V_NUM = 10; --自增 V_NUM := V_NUM + 1; END LOOP; COMMIT; END; / --调用存储过程 EXEC SP_PRO6('ALLEN');
循环语句-while循环:
CREATE OR REPLACE PROCEDURE SP_PRO6(SPNAME VARCHAR2) IS --定义 :=表示赋值 V_NUM NUMBER := 11; BEGIN WHILE V_NUM <= 20 LOOP --执行 INSERT INTO USERS VALUES (V_NUM, SPNAME); V_NUM := V_NUM + 1; END LOOP; COMMIT; END; / --调用存储过程 EXEC SP_PRO6('ALLEN');
--调用存储过程
EXEC SP_PRO6('ALLEN');
循环语句-for循环:
CREATE OR REPLACE PROCEDURE SP_PRO6 IS--注意如果无参记得不要加() BEGIN FOR I IN REVERSE 1 .. 10 LOOP --REVERSE反转函数,表示I从10到1递减,去掉REVERSE表示I从1到10递增 INSERT INTO USERS VALUES (I, 'shunping'); END LOOP; END; / --调用存储过程 EXEC SP_PRO6;
顺序控制语句-goto、null:
set serveroutput on; DECLARE I INT := 1; BEGIN LOOP DBMS_OUTPUT.PUT_LINE('输出i=' || I); IF I = 1 THEN GOTO END_LOOP; END IF; I := I + 1; END LOOP; <<END_LOOP>> DBMS_OUTPUT.PUT_LINE('循环结束'); END; /
SET serveroutput ON; DECLARE V_SAL EMP.SAL%TYPE; V_ENAME EMP.ENAME%TYPE; BEGIN SELECT ENAME, SAL INTO V_ENAME, V_SAL FROM EMP WHERE EMPNO = &NO; IF V_SAL < 3000 THEN UPDATE EMP SET COMM = SAL * 0.1 WHERE ENAME = V_ENAME; dbms_output.put_line('1111'); ELSE NULL; dbms_output.put_line('2222');--不会被执行 END IF; END; /
8:pl/sql分页
一、无返回值的存储过程
古人云:欲速则不达,为了让大家伙比较容易接受分页过程编写,我还是从简单到复杂,循序渐进的给大家讲解。首先是掌握最简单的存储过程,无返回值的存储过程。 案例:现有一张表book,表结构如下:书号、书名、出版社。
CREATE TABLE book( ID NUMBER(4), book_name VARCHAR2(30), publishing VARCHAR2(30) );
请写一个过程,可以向book表添加书,要求通过java程序调用该过程。
--注意:in->表示这是一个输入参数,默认为in --out->表示一个输出参数 CREATE OR REPLACE PROCEDURE ADD_BOOK(ID IN NUMBER, NAME IN VARCHAR2, PUBLISHING IN VARCHAR2) IS BEGIN INSERT INTO BOOK VALUES (ID, NAME, PUBLISHING); COMMIT; END; /
java程序调用该存储过程的代码
package junit.test; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; /** * 调用一个无返回值的存储过程 * * @author jiqinlin * */ public class ProcedureTest { public static void main(String[] args) { try { // 1.加载驱动 Class.forName("oracle.jdbc.driver.OracleDriver"); // 2.得到连接 Connection ct = DriverManager.getConnection( "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "oracle"); // 3.创建CallableStatement CallableStatement cs = ct.prepareCall("call ADD_BOOK(?,?,?)"); //给?赋值 cs.setInt(1, 1); cs.setString(2, "java"); cs.setString(3, "java出版社"); // 4.执行 cs.execute(); //5、关闭 cs.close(); ct.close(); } catch (Exception e) { e.printStackTrace(); } } }
二、有返回值的存储过程(非列表)
案例:编写一个存储过程,可以输入雇员的编号,返回该雇员的姓名。
--输入和输出的存储过程 CREATE OR REPLACE PROCEDURE SP_PROC(SPNO IN NUMBER, SPNAME OUT VARCHAR2) IS BEGIN SELECT ENAME INTO SPNAME FROM EMP WHERE EMPNO = SPNO; END; /
java程序调用该存储过程的代码
package junit.test; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; /** * 调用一个无返回值的存储过程 * * @author jiqinlin * */ public class ProcedureTest { public static void main(String[] args) { try { // 1.加载驱动 Class.forName("oracle.jdbc.driver.OracleDriver"); // 2.得到连接 Connection ct = DriverManager.getConnection( "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "oracle"); // 3.创建CallableStatement CallableStatement cs = ct.prepareCall("{call sp_proc(?,?)}"); //给第一个?赋值 cs.setInt(1,7788); //给第二个?赋值 cs.registerOutParameter(2,oracle.jdbc.OracleTypes.VARCHAR); //4、执行 cs.execute(); //取出返回值,要注意?的顺序 String name=cs.getString(2); System.out.println("编号7788的名字:"+name); //5、关闭 cs.close(); ct.close(); } catch (Exception e) { e.printStackTrace(); } } }
案例扩张:编写一个过程,可以输入雇员的编号,返回该雇员的姓名、工资和岗位。
--输入和输出的存储过程 CREATE OR REPLACE PROCEDURE SP_PROC(SPNO IN NUMBER, SPNAME OUT VARCHAR2, SPSAL OUT NUMBER, SPJOB OUT VARCHAR2) IS BEGIN SELECT ENAME, SAL, JOB INTO SPNAME, SPSAL, SPJOB FROM EMP WHERE EMPNO = SPNO; END; /
java程序调用该存储过程的代码
package junit.test; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; /** * 调用一个无返回值的存储过程 * * @author jiqinlin * */ public class ProcedureTest { public static void main(String[] args) { try { // 1.加载驱动 Class.forName("oracle.jdbc.driver.OracleDriver"); // 2.得到连接 Connection ct = DriverManager.getConnection( "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "oracle"); // 3.创建CallableStatement CallableStatement cs = ct.prepareCall("{call sp_proc(?,?,?,?)}"); //给第一个?赋值 cs.setInt(1,7788); //给第二个?赋值 cs.registerOutParameter(2,oracle.jdbc.OracleTypes.VARCHAR); //给第三个?赋值 cs.registerOutParameter(3,oracle.jdbc.OracleTypes.DOUBLE); //给第四个?赋值 cs.registerOutParameter(4,oracle.jdbc.OracleTypes.VARCHAR); //4、执行 cs.execute(); //取出返回值,要注意?的顺序 String name=cs.getString(2); double sal=cs.getDouble(3); String job=cs.getString(4); System.out.println("编号7788的名字:"+name+",职位:"+job+",薪水:"+sal+""); //5、关闭 cs.close(); ct.close(); } catch (Exception e) { e.printStackTrace(); } } }
三、有返回值的存储过程(列表[结果集])
案例:编写一个存储过程,输入部门号,返回该部门所有雇员信息。
该题分析如下:由于oracle存储过程没有返回值,它的所有返回值都是通过out参数来替代的,列表同样也不例外,但由于是集合,所以不能用一般的参数,必须要用pagkage了。所以要分两部分:
1)、建立一个包,在该包中我们定义类型test_cursor,它是个游标。
CREATE OR REPLACE PACKAGE TESTPACKAGE AS TYPE TEST_CURSOR IS REF CURSOR; END TESTPACKAGE; /
2)、建立存储过程。
CREATE OR REPLACE PROCEDURE SP_PROC(SPNO IN NUMBER, P_CURSOR OUT TESTPACKAGE.TEST_CURSOR) IS BEGIN OPEN P_CURSOR FOR SELECT * FROM EMP WHERE DEPTNO = SPNO; END SP_PROC; /
3)、如何在java 程序中调用该过程
package junit.test; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; /** * 调用一个无返回值的存储过程 * * @author jiqinlin * */ public class ProcedureTest { public static void main(String[] args) { try { // 1.加载驱动 Class.forName("oracle.jdbc.driver.OracleDriver"); // 2.得到连接 Connection ct = DriverManager.getConnection( "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "oracle"); // 3.创建CallableStatement CallableStatement cs = ct.prepareCall("{call sp_proc(?,?)}"); //给第一个?赋值 cs.setInt(1,10); //给第二个?赋值 cs.registerOutParameter(2,oracle.jdbc.OracleTypes.CURSOR); //4、执行 cs.execute(); //得到结果集 ResultSet rs = (ResultSet) cs.getObject(2); while (rs.next()) { System.out.println(rs.getInt(1) + " " + rs.getString(2)); } //5、关闭 rs.close(); cs.close(); ct.close(); } catch (Exception e) { e.printStackTrace(); } } }
四、编写分页过程
有了上面的基础,相信大家可以完成分页存储过程了。
要求,请大家编写一个存储过程,要求可以输入表名、每页显示记录数、当前页。返回总记录数,总页数,和返回的结果集。
--ROWNUM用法 SELECT o.*, ROWNUM RN FROM (SELECT * FROM EMP) o WHERE ROWNUM <= 10; ----oracle分页sql语句;在分页时,大家可以把下面的sql语句当做一个模板使用 SELECT * FROM (SELECT o.*, ROWNUM RN FROM (SELECT * FROM EMP) o WHERE ROWNUM <= 10) WHERE RN >= 6;
1)、开发一个包
建立一个包,在该包中定义类型为test_cursor的游标。
--建立一个包 CREATE OR REPLACE PACKAGE TESTPACKAGE AS TYPE TEST_CURSOR IS REF CURSOR; END TESTPACKAGE; / --开始编写分页的过程 CREATE OR REPLACE PROCEDURE FENYE(TABLENAME IN VARCHAR2, PAGESIZE IN NUMBER, --每页显示记录数 PAGENOW IN NUMBER, --页数 MYROWS OUT NUMBER, --总记录数 MYPAGECOUNT OUT NUMBER, --总页数 P_CURSOR OUT TESTPACKAGE.TEST_CURSOR) IS --返回的记录集 --定义部分 --定义sql语句字符串 V_SQL VARCHAR2(1000); --定义两个整数 V_BEGIN NUMBER := (PAGENOW - 1) * PAGESIZE + 1; V_END NUMBER := PAGENOW * PAGESIZE; BEGIN --执行部分 V_SQL := 'select * from (select t1.*, rownum rn from (select * from ' || TABLENAME || ') t1 where rownum<=' || V_END || ') where rn>=' || V_BEGIN; --把游标和sql关联 OPEN P_CURSOR FOR V_SQL; --计算myrows和myPageCount --组织一个sql语句 V_SQL := 'select count(*) from ' || TABLENAME; --执行sql,并把返回的值,赋给myrows; EXECUTE ImMEDIATE V_SQL INTO MYROWS; --它解析并马上执行动态的SQL语句或非运行时创建的PL/SQL块.动态创建和执行SQL语句性能超前, --EXECUTE IMMEDIATE的目标在于减小企业费用并获得较高的性能,较之以前它相当容易编码. --尽管DBMS_SQL仍然可用,但是推荐使用EXECUTE IMMEDIATE,因为它获的收益在包之上。 --计算myPageCount --if myrows%Pagesize=0 then 这样写是错的 IF MOD(MYROWS, PAGESIZE) = 0 THEN MYPAGECOUNT := MYROWS/PAGESIZE; ELSE MYPAGECOUNT := MYROWS/PAGESIZE + 1; END IF; --关闭游标 --CLOSE P_CURSOR; --不要关闭,否则java调用该存储过程会报错 END; /
java调用分页代码
package junit.test; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; /** * 调用一个无返回值的存储过程 * * @author jiqinlin * */ public class ProcedureTest { public static void main(String[] args) { try { // 1.加载驱动 Class.forName("oracle.jdbc.driver.OracleDriver"); // 2.得到连接 Connection ct = DriverManager.getConnection( "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "oracle"); // 3.创建CallableStatement CallableStatement cs = ct.prepareCall("{call fenye(?,?,?,?,?,?)}"); cs.setString(1, "emp"); //表名 cs.setInt(2, 5); //每页显示记录数 cs.setInt(3, 1);//页数 // 注册总记录数 cs.registerOutParameter(4, oracle.jdbc.OracleTypes.INTEGER); //总记录数 // 注册总页数 cs.registerOutParameter(5, oracle.jdbc.OracleTypes.INTEGER); //总页数 // 注册返回的结果集 cs.registerOutParameter(6, oracle.jdbc.OracleTypes.CURSOR); //返回的记录集 // 4、执行 cs.execute(); // 得到结果集 // 取出总记录数 /这里要注意,getInt(4)中4,是由该参数的位置决定的 int rowNum = cs.getInt(4); int pageCount = cs.getInt(5); ResultSet rs = (ResultSet) cs.getObject(6); // 显示一下,看看对不对 System.out.println("rowNum=" + rowNum); System.out.println("总页数=" + pageCount); while (rs.next()) { System.out.println("编号:" + rs.getInt(1) + " 名字:" + rs.getString(2) + " 工资:" + rs.getFloat(6)); } // 5、关闭 //rs.close(); cs.close(); ct.close(); } catch (Exception e) { e.printStackTrace(); } } }
十五:oracle例外
1)、预定义例外用于处理常见的oracle错误。
2)、非预定义例外用于处理预定义例外不能处理的例外。
3)、自定义例外用于处理与oracle错误无关的其它情况。
1:处理预定义例外
case_not_found预定义例外 :
SET SERVEROUTPUT ON; CREATE OR REPLACE PROCEDURE SP_PRO6(SPNO NUMBER) IS V_SAL EMP.SAL%TYPE; BEGIN SELECT SAL INTO V_SAL FROM EMP WHERE EMPNO = SPNO; CASE WHEN V_SAL < 1000 THEN UPDATE EMP SET SAL = SAL + 100 WHERE EMPNO = SPNO; WHEN V_SAL < 2000 THEN UPDATE EMP SET SAL = SAL + 200 WHERE EMPNO = SPNO; END CASE; EXCEPTION WHEN CASE_NOT_FOUND THEN DBMS_OUTPUT.PUT_LINE('case语句没有与' || V_SAL || '相匹配的条件'); END; / --调用存储过程 SQL> EXEC SP_PRO6(7369); case语句没有与4444相匹配的条件
cursor_already_open预定义例外 :
DECLARE CURSOR EMP_CURSOR IS SELECT ENAME, SAL FROM EMP; BEGIN OPEN EMP_CURSOR; --声明时游标已打开,所以没必要再次打开 FOR EMP_RECORD1 IN EMP_CURSOR LOOP DBMS_OUTPUT.PUT_LINE(EMP_RECORD1.ENAME); END LOOP; EXCEPTION WHEN CURSOR_ALREADY_OPEN THEN DBMS_OUTPUT.PUT_LINE('游标已经打开'); END; /
dup_val_on_index预定义例外 :
BEGIN INSERT INTO DEPT VALUES (10, '公关部', '北京'); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN DBMS_OUTPUT.PUT_LINE('在deptno列上不能出现重复值'); END; /
invalid_cursorn预定义例外 :
DECLARE CURSOR EMP_CURSOR IS SELECT ENAME, SAL FROM EMP; EMP_RECORD EMP_CURSOR%ROWTYPE; BEGIN --open emp_cursor; --打开游标 FETCH EMP_CURSOR INTO EMP_RECORD; DBMS_OUTPUT.PUT_LINE(EMP_RECORD.ENAME); CLOSE EMP_CURSOR; EXCEPTION WHEN INVALID_CURSOR THEN DBMS_OUTPUT.PUT_LINE('请检测游标是否打开'); END; /
invalid_number预定义例外 :
SET SERVEROUTPUT ON; BEGIN UPDATE EMP SET SAL = SAL + 'AAA'; EXCEPTION WHEN INVALID_NUMBER THEN DBMS_OUTPUT.PUT_LINE('输入的数字不正确'); END; /
no_data_found预定义例外 :
SET serveroutput ON; DECLARE V_SAL EMP.SAL%TYPE; BEGIN SELECT SAL INTO V_SAL FROM EMP WHERE ENAME = 'ljq'; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('不存在该员工'); END; /
too_many_rows预定义例外 :
DECLARE V_ENAME EMP.ENAME%TYPE; BEGIN SELECT ENAME INTO V_ENAME FROM EMP; EXCEPTION WHEN TOO_MANY_ROWS THEN DBMS_OUTPUT.PUT_LINE('返回了多行'); END; /
zero_divide预定义例外 : 当执行2/0语句时,则会触发该例外
value_error预定义例外 : 当在执行赋值操作时,如果变量的长度不足以容纳实际数据,则会触发该例外value_error
login_denied : 当用户非法登录时,会触发该例外
not_logged_on:如果用户没有登录就执行dml操作,就会触发该例外
storage_error:如果超过了内存空间或是内存被损坏,就触发该例外
timeout_on_resource:如果oracle在等待资源时,出现了超时就触发该例外
2:非预定义例外:非预定义例外用于处理与预定义例外无关的oracle错误。使用预定义例外只能处理21个oracle 错误,而当使用pl/sql开发应用程序时,可能会遇到其它的一些oracle错误。比如在pl/sql块中执行dml语句时,违反了约束规定等等。
3:处理预定义例外: 预定义例外和自定义例外都是与oracle错误相关的,并且出现的oracle 错误会隐含的触发相应的例外;而自定义例外与oracle 错误没有任何关联,它是由开发人员为特定情况所定义的例外.
CREATE OR REPLACE PROCEDURE EX_TEST(SPNO NUMBER) IS BEGIN UPDATE EMP SET SAL = SAL + 1000 WHERE EMPNO = SPNO; END; / --调用存储过程, EXEC EX_TEST(56);
CREATE OR REPLACE PROCEDURE EX_TEST(SPNO NUMBER) IS --定义一个例外 MYEX EXCEPTION; BEGIN --更新用户sal UPDATE EMP SET SAL = SAL + 1000 WHERE EMPNO = SPNO; --sql%notfound 这是表示没有update --raise myex;触发myex IF SQL%NOTFOUND THEN RAISE MYEX; END IF; EXCEPTION WHEN MYEX THEN DBMS_OUTPUT.PUT_LINE('没有更新任何用户'); END; /