/--PL/SQL语句吕晓东 08:52:32 (多人发送) --以sysdba身份登录 C:\>sqlplus / as sysdba --创建表空间 SQL> CREATE SMALLFILE TABLESPACE TBS_DRTEST DATAFILE 'E:\oradata\orcl\tbs_drtest.dbf' SIZE 10M AUTOEXTEND ON; Tablespace created. --创建用户 drtest/drsoft CREATE USER drtest PROFILE DEFAULT IDENTIFIED BY drsoft DEFAULT TABLESPACE tbs_drtest TEMPORARY TABLESPACE TEMP ACCOUNT UNLOCK; User created. --给用户指定表空间 alter user drtest default tablespace tbs_drtest; --授权 grant connect,resource to drtest; User created. --用drtest/当然soft登录 SQL> conn drtest/drsoft Connected. --创建表 CREATE TABLE emp ( id int NOT NULL, hiredate date not null, PRIMARY KEY (id) ) tablespace tbs_drtest; -----用户drtest编写PL/SQL程序块 SQL> conn drtest/drsoft Connected. SQL> set serveroutput on //打开服务器的输出显示,即打开oracle自带的输出方法dbms_output。 SQL> declare 2 l_text varchar2(100); 3 begin 4 l_text := 'Hello, World!'; 5 dbms_output.put_line(l_text); 6 exception 7 when others then 8 dbms_output.put_line('We encountered an exception!'); 9 raise; 10 end; 11 / Hello, World! PL/SQL procedure successfully completed. -------%type和%rowtype来声明变量 ---- PL/SQL集合——记录 SQL> conn scott/tiger Connected. SQL> show user USER is "SCOTT" SQL> desc dept Name Null? Type ----------------------------------------- -------- ------------------ DEPTNO NOT NULL NUMBER(2) DNAME VARCHAR2(14) LOC VARCHAR2(13) SQL> set serverout on SQL> declare 2 type dept_record_type is record( 3 deptno number, 4 dname varchar2(14), 5 loc varchar2(13) 6 ); 7 l_my_dept dept_record_type; 8 begin 9 l_my_dept.deptno := 80; 10 l_my_dept.dname := 'dev'; 11 l_my_dept.loc := 'TangShan'; 12 dbms_output.put_line('MyDept is :'); 13 dbms_output.put_line(l_my_dept.deptno); 14 dbms_output.put_line(l_my_dept.dname); 15 dbms_output.put_line(l_my_dept.loc); 16 end; 17 / MyDept is : 80 dev TangShan
PL/SQL procedure successfully completed.
--使用%type和%rowtype来声明变量,而不必规定特定的数据类型 SQL> declare 2 type dept_rec_type is record( 3 deptno dept.deptno%type, 4 deptname dept.dname%type, 5 deptloc dept.loc%type 6 ); 7 mydept dept_rec_type; 8 begin 9 mydept.deptno := 90; 10 mydept.deptname := 'hr'; 11 mydept.deptloc := 'tangshang'; 12 insert into dept values(mydept.deptno,mydept.deptname,mydept.deptloc); 13 end; 14 / PL/SQL procedure successfully completed. SQL> select * from dept; DEPTNO DNAME LOC ---------- -------------- ------------- 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS 30 SALES CHICAGO 40 OPERATIONS BOSTON 90 hr tangshang SQL> ---游标 SQL> set serveroutput on SQL> SQL> declare 2 cursor emp_cur(deptid in number) 3 is select * from emp where deptno = deptid; 4 5 l_emp emp%rowtype; 6 begin 7 -- 打开游标 8 open emp_cur(30); 9 10 -- 通过游标遍历数据集 11 loop 12 fetch emp_cur into l_emp; -- 获取一条数据到l_emp 13 exit when emp_cur%notfound; 14 15 dbms_output.put_line(TO_CHAR(l_emp.empno) || ',' || l_emp.ename || ',' || TO_CHAR(l_emp.deptno)); 16 end loop; 17 18 -- 关闭游标 19 close emp_cur; 20 end; 21 / 7499,ALLEN,30 7521,WARD,30 7654,MARTIN,30 7698,BLAKE,30 7844,TURNER,30 7900,JAMES,30 PL/SQL procedure successfully completed. SQL> -------用游标实现查询工资是1500以上的员工信息 SQL> set serveroutput on --设置打印输出参数,on表示打印输出显示 SP2-0735: unknown SET option beginning "--设置打印..." SQL> SQL> declare 2 tmpsal scott.emp.sal%type; 3 cursor csemp is 4 select * from scott.emp where sal >= tmpsal order by sal; 5 cursorrec csemp%rowtype; 6 begin 7 tmpsal := 1500; 8 open csemp; --打开游标 9 fetch csemp into cursorrec; 10 dbms_output.put_line('员工编号 姓名 工资 部门编号'); 11 while(csemp%found) loop 12 dbms_output.put_line(to_char(cursorrec.empno) || ' ' || cursorrec.ename || ' ' || to_char(cursorrec.sal) || ' ' || to_char(cursorrec.deptno)); 13 fetch csemp into cursorrec; --移动游标获取下一条纪录到cursorrec变量 14 end loop; 15 close csemp; --关闭游标 16 end; 17 / 员工编号 姓名 工资 部门编号 7844 TURNER 1500 30 7499 ALLEN 1600 30 7782 CLARK 2450 10 7698 BLAKE 2850 30 7566 JONES 2975 20 7788 SCOTT 3000 20 7902 FORD 3000 20 7839 KING 5000 10 PL/SQL procedure successfully completed. ---------------------------------------------------------------------------------- --隐式游标 set serveroutput on declare l_deptno dept.deptno%type; begin l_deptno := 30; for mydept in (select empno, ename, sal from emp where deptno = l_deptno) loop dbms_output.put_line(to_char(mydept.empno) || ',' || mydept.ename || ',' || mydept.sal); end loop; end; / -----------------游标 吕晓东 14:49:53 (多人发送) --无参游标 set serveroutput on DECLARE v_name emp.ename%TYPE; v_job emp.job%TYPE; CURSOR c1 --声明游标,没有参数没有返回值 IS SELECT ename, job FROM emp WHERE deptno = 20; BEGIN OPEN c1; --打开游标 LOOP FETCH c1 INTO v_name, v_job; --提取游标 IF c1%FOUND THEN DBMS_OUTPUT.PUT_LINE(v_name||'的岗位是'||v_job); ELSE DBMS_OUTPUT.PUT_LINE('已经处理完结果集了'); EXIT; END IF; END LOOP; CLOSE c1; --关闭游标 END; / ----------------------------------------------------- --有参游标 set serveroutput on DECLARE v_name emp.ename%TYPE; v_firedate emp.hiredate%TYPE; CURSOR c2(dept_id NUMBER, jobname VARCHAR2) --声明游标,有参数没有返回值 IS SELECT ename, hiredate FROM emp WHERE deptno = dept_id AND job = jobname; BEGIN OPEN c2(20, 'ANALYST'); --打开游标,传递参数值 LOOP FETCH c2 INTO v_name, v_firedate; --提取游标 IF c2%FOUND THEN DBMS_OUTPUT.PUT_LINE(v_name||'的雇佣日期是'||v_firedate); ELSE DBMS_OUTPUT.PUT_LINE('已经处理完结果集了'); EXIT; END IF; END LOOP; CLOSE c2; --关闭游标 END; / ----------------------------------------------------- --自定义记录类型的游标 set serveroutput on DECLARE TYPE emp_record_type IS RECORD( e_name emp.ename%TYPE, h_date emp.hiredate%TYPE); v_emp_record EMP_RECORD_TYPE; CURSOR c3(dept_id NUMBER, j_id VARCHAR2) --声明游标,有参数有返回值 RETURN EMP_RECORD_TYPE IS SELECT ename, hiredate FROM emp WHERE deptno = dept_id AND job = j_id; BEGIN OPEN c3(j_id => 'MANAGER', dept_id => 10); --打开游标,传递参数值 LOOP FETCH c3 INTO v_emp_record; --提取游标 IF c3%FOUND THEN DBMS_OUTPUT.PUT_LINE(v_emp_record.e_name||'的雇佣日期是' ||v_emp_record.h_date); ELSE DBMS_OUTPUT.PUT_LINE('已经处理完结果集了'); EXIT; END IF; END LOOP; CLOSE c3; --关闭游标 END; / ----------------------------------------------------- set serveroutput on DECLARE CURSOR c4(dept_id NUMBER, j_id VARCHAR2) --声明游标,有参数没有返回值 IS SELECT e_name, hiredate FROM emp WHERE deptno = dept_id AND job = j_id; --基于游标定义记录变量,比声明记录类型变量要方便,不容易出错 v_emp_record c4%ROWTYPE; BEGIN OPEN c4(30, 'SALESMAN'); --打开游标,传递参数值 LOOP FETCH c4 INTO v_emp_record; --提取游标 IF c4%FOUND THEN DBMS_OUTPUT.PUT_LINE(v_emp_record.e_name || '的雇佣日期是' ||v_emp_record.hiredate); ELSE DBMS_OUTPUT.PUT_LINE('已经处理完结果集了'); EXIT; END IF; END LOOP; CLOSE c4; --关闭游标 END; / ----------------------------------------------------- -----异常 吕晓东 11:39:17 (多人发送) /---自定义异常 declare l_count number; exp_too_less exception; begin select count(*) into l_count from emp where deptno=30; if l_count <= 6 then raise exp_too_less; end if; exception when NO_DATA_FOUND then dbms_output.put_line('没有找到数据'); when exp_too_less then dbms_output.put_line('数据记录太少'); when others then dbms_output.put_line('插入数据异常'); end; / -------case例子 吕晓东 11:53:59 (多人发送) declare l_num dept.deptno%type; begin for mydept_rec in (select * from dept) loop l_num := mydept_rec.deptno; case l_num when 10 then dbms_output.put_line(to_char(l_num) || ', ACCOUNTING'); when 20 then dbms_output.put_line(to_char(l_num) || ', RESEARCH'); when 30 then dbms_output.put_line(to_char(l_num) || ', SALES'); else dbms_output.put_line(to_char(l_num) || ', unknown'); end case; end loop; exception when NO_DATA_FOUND then dbms_output.put_line('没有找到数据'); when others then dbms_output.put_line('插入数据异常'); end; / -----存储过程的创建和执行 SQL> set serveroutput on SQL> create procedure myproc as 2 begin 3 dbms_output.put_line('欢迎学习存储过程'); 4 end myproc; 5 / Procedure created. SQL> begin 2 myproc; 3 end; 4 / 欢迎学习存储过程 PL/SQL procedure successfully completed. SQL> ------a创建mark用户并授权 SQL> conn system/drsoft Connected. SQL> show user USER is "SYSTEM" SQL> create user mark identified by mark 2 default tablespace tbs_drtest 3 temporary tablespace temp 4 account unlock 5 / User created. SQL> grant connect, resource to mark; Grant succeeded. SQL> conn mark/mark; Connected. SQL> show user USER is "MARK" SQL> create user chris identified by chris 2 default tablespace tbs_drtest 3 temporary tablespace temp 4 account unlock 5 / User created. SQL> conn system/drsoft Connected. SQL> show user USER is "SYSTEM" SQL> create user sean identified by sean 2 default tablespace tbs_drtest 3 temporary tablespace temp 4 account unlock 5 / User created. SQL> grant connect, resource to sean; Grant succeeded. --b用mark用户创建存储过程 SQL> show user USER is "MARK" SQL> set serveroutput on SQL> create procedure marks_proc as 2 begin 3 dbms_output.put_line('欢迎学习存储过程'); 4 end; 5 / Procedure created. ---c用chris用户执行存储过程会出错因为没有授权 SQL> conn chris/chris; Connected. SQL> show user USER is "CHRIS" SQL> execute mark.marks_proc; BEGIN mark.marks_proc; END; * ERROR at line 1: ORA-06550: line 1, column 7: PLS-00201: identifier 'MARK.MARKS_PROC' must be declared ORA-06550: line 1, column 7: PL/SQL: Statement ignored ----d mark用户为chris用户授予execute权限 SQL> conn mark/mark Connected. SQL> show user USER is "MARK" SQL> grant execute on marks_proc to chris; Grant succeeded. ----e chris再次执行存储过程mark.marks_proc SQL> conn chris/chris Connected. SQL> show user USER is "CHRIS" SQL> set serveroutput on SQL> execute mark.marks_proc; 欢迎学习存储过程 PL/SQL procedure successfully completed. -----f用sean执行mark.marks_proc SQL> conn sean/sean Connected. SQL> show user USER is "SEAN" SQL> excute mark.marks_proc; SP2-0734: unknown command beginning "excute mar..." - rest of line ignored. SQL> execute mark.marks_proc; BEGIN mark.marks_proc; END; * ERROR at line 1: ORA-06550: line 1, column 7: PLS-00201: identifier 'MARK.MARKS_PROC' must be declared ORA-06550: line 1, column 7: PL/SQL: Statement ignored ----g mark把存储过程的权限授予PUBLIC,这样其他用户就可以执行mark的存储过程 SQL> conn mark/mark Connected. SQL> show user USER is "MARK" SQL> grant execute on marks_proc to public; Grant succeeded. ----h 再用sean执行mark.marks_proc SQL> conn sean/sean Connected. SQL> show user USER is "SEAN" SQL> set serveroutput on SQL> execute mark.marks_proc; 欢迎学习存储过程 PL/SQL procedure successfully completed. ---带参数的存储过程 吕晓东 16:53:42 (多人发送) create or replace procedure p_insert_dept( dept_no in number, dept_name in varchar2, dept_loc in varchar2 ) is begin dbms_output.put_line('deptno:' || dept_no || '; dname:' || dept_name || '; loc:' || dept_loc); insert into dept values(dept_no,dept_name,dept_loc); end; / 项目开发中对存储过程应用存储过程的案例。 吕晓东 09:27:18 (多人发送) 1、表结构 电压等级表: create table T_VOL_LEVEL ( VID NUMBER(2) not null, --id VOLTAGE VARCHAR2(30) not null, --电压 400V / 10KV VOL_SIMP CHAR(2) not null ----电压等级简拼 DY / GY ) 工程编号表 create table T_PROJECT_NUMBER ( NUM_ID NUMBER(8) not null, VOL_SIMP CHAR(2) not null, --电压等级简拼 DY / GY YEAR CHAR(4) not null, --年份 SEQ_NUM NUMBER(5) not null --流水号 ) 创建工程编号主键序列: -- Create sequence create sequence SEQ_PROJECT_NUM minvalue 1 maxvalue 9999 start with 6 increment by 1 cache 5; 2、生成工程编号规则:电压等级 + '-' + 当前年 + 五位流水 --创建存储过程 create or replace procedure p_get_project_apply_num(voltage_level_id in number, project_apply_number out varchar2) is yy char(4); yy_temp number(4); vl_sim char(2); n number(1); curr_seq_num number(5); begin select extract(year from sysdate) into yy_temp from dual; yy := to_char(yy_temp); dbms_output.put_line('当前年:' || yy); select VOL_SIMP into vl_sim FROM T_VOL_LEVEL where VID=voltage_level_id; dbms_output.put_line('电压等级:' || vl_sim); select count(1) into n from T_PROJECT_NUMBER where YEAR=yy and VOL_SIMP=vl_sim; dbms_output.put_line('对应工程申请编号记录:' || to_char(n)); if n <= 0 then --新增一条当前年对应电压等级的工程编号记录,格式: DY-201100001 project_apply_number := vl_sim || '-' || yy || lpad('1',5,'0'); insert into T_PROJECT_NUMBER values(SEQ_PROJECT_NUM.NEXTVAL, vl_sim, yy, 2); else --更新当前年和指定电压等级的工程编号的序列号 select SEQ_NUM into curr_seq_num from T_PROJECT_NUMBER where YEAR=yy and VOL_SIMP=vl_sim; project_apply_number := vl_sim || '-' || yy || lpad(to_char(curr_seq_num),5,'0'); update T_PROJECT_NUMBER set SEQ_NUM=SEQ_NUM+1 where YEAR=yy and VOL_SIMP=vl_sim; end if; commit; dbms_output.put_line('获取工程申请编号:' || project_apply_number); exception when NO_DATA_FOUND then rollback; dbms_output.put_line('data not found:vl_sim=' || vl_sim); when others then rollback; dbms_output.put_line('未知错误'); end p_get_project_apply_num; --PL/SQL测试 declare voltage_level_id number(2); project_apply_number varchar2(11); begin -- Call the procedure voltage_level_id := 1; --低压 p_get_project_apply_num(voltage_level_id, project_apply_number); end; 3、JDBC 调用存储过程 public class ProjectApplyDaoImpl implements ProjectApplyDao { ...... private SimpleJdbcCall procReadPAN; //调用获取工程申请编号存储过程 @Resource(name="dataSource") public void setDataSource(DataSource dataSource){ this.procReadPAN = new SimpleJdbcCall(dataSource).withProcedureName("p_get_project_apply_num"); //初始化SimpleJdbcCall } @SuppressWarnings("unchecked") @Override public String getProjecdtApplyNum(short vid) throws ProjectApplyException { //SimpleJdbcCall 调用存储过程的使用方法参考spring官方参考手册11章相关部分 //voltage_level_id 为存储过程 p_get_project_apply_num(voltage_level_id in number, project_apply_number out varchar2)中的输入参数名 SqlParameterSource in = new MapSqlParameterSource().addValue("voltage_level_id", vid); log.debug(in.getValue("voltage_level_id")); Map out = this.procReadPAN.execute(in); log.debug(out); //注意SQL中返回的都是大写的,刚开始弄成小写的就取不到,但是数据库变化了说明这里有问题,通过日志打印发现是大写,罪过罪过 String pan = (String) out.get("PROJECT_APPLY_NUMBER"); log.debug("获取的工程编号:" + pan); return pan; } } ----------------- 吕晓东 17:01:54 (多人发送) create or replace procedure p_get_dept( dept_no in number, dept_name out dept.dname%type, --使用%type表示变量不需要确定的类型 dept_loc out dept.loc%type ) is begin select dname into dept_name from dept where deptno=dept_no; select loc into dept_loc from dept where deptno=dept_no; dbms_output.put_line('deptno:' || dept_no || '; dname:' || dept_name || '; loc:' || dept_loc); end; declare dn varchar2(14); dl varchar2(13); begin p_get_dept(10, dn, dl); dbms_output.put_line('dn=' || dn || '; dl=' || dl); end; / 吕晓东 17:16:52 (多人发送) 1、编写函数f_get_emp_count,要求根据输入参数deptno查找对应部门的员工总数。 create or replace function f_get_emp_count(dept_no in number) return number as l_count number(2); begin dbms_output.put_line('deptno:' || to_char(dept_no)); select count(1) into l_count from emp where deptno=dept_no; return l_count; exception when NO_DATA_FOUND then dbms_output.put_line('no data found: deptno=' + dept_no); when others then dbms_output.put_line('other exception: no data found: deptno=' + dept_no); end; 客户端sqlplus测试: declare l_deptno number(2); l_num number(2) default 0; begin l_deptno := 10; l_num := f_get_emp_count(l_deptno); dbms_output.put_line('emp count is:' || to_char(l_num)); end; / 按照输入的部门编号查询指定部门的工资总和,若没有该部门需要异常处理。同时OUT形参输出该部门的人数总和。 代码: CREATE OR REPLACE FUNCTION getSalaryByDeptNo(v_deptno in emp.deptno%type,v_empcount out number) RETURN NUMBER AS 吕晓东 17:59:46 (多人发送) 示例: 1、用序列和过程创建一个系统流水号,要求格式为:当前时间(业务代码+yyyymmddhhmiss+5位流水号(不足用0补充)),如:ZZHZ2010060611093600001 知识点: a、日期转化函数 to_char b、字符串连接 || c、 字符串补全 lpad ------------------------------------------------- 代码: --序列 create sequence seq_lsh minvalue 1 maxvalue 99999 start with 1 increment by 1 cache 20 cycle order; --过程 CREATE OR REPLACE PROCEDURE gen_lsh(v_ywdm in char,v_lsh out char) IS v_datestr varchar2(14); v_lshstr varchar2(5); v_lshhz number; exp_ywdm EXCEPTION; BEGIN if length(v_ywdm) != 4 then raise exp_ywdm; end if; SELECT to_char(sysdate,'yyyymmddhh24miss') INTO v_datestr FROM dual; select seq_lsh.nextval into v_lshhz from dual; v_lshstr := lpad(v_lshhz,5,'0'); v_lsh := v_ywdm || v_datestr || v_lshstr; EXCEPTION WHEN exp_ywdm THEN DBMS_OUTPUT.PUT_LINE('业务代码长度应为4位字母:' || v_ywdm); END gen_lsh; / 2、创建一个id序列生成器sq_id_book,用来产生id值,创建一个表t_book(id number(4) not null primary key, name varchar2(30)),要求t_book表的id从序列中获取. 测试:用insert into 保存新纪录。如:insert into t_book(id,name) values(sq_id_book.nextval, 'java'); --序列 create sequence sq_id_book minvalue 1 maxvalue 99999 start with 1 increment by 1 cache 20 cycle order; --创建表 create table t_book( id number not null primary key, name varchar2(30) ) / --插入数据 insert into t_book(id,name) values(sq_id_book.nextval, 'java'); insert into t_book(id,name) values(sq_id_book.nextval, 'oracle'); insert into t_book(id,name) values(sq_id_book.nextval, 'jquery'); -------------------包 吕晓东 15:42:20 (多人发送) --创建包的规范 create or replace package emp_pkg as --根据部门编号获得部门名称 procedure p_get_dept_info(dept_no in number, deptname out varchar2); --根据部门编号获得部门的总人数 function f_get_emp_num(dept_no number) return number; end emp_pkg; / --创建包的主体 create or replace package body emp_pkg as procedure p_get_dept_info(dept_no in number, deptname out varchar2) is begin select dname into deptname from dept where deptno=dept_no; exception when NO_DATA_FOUND then dbms_output.put_line('invalid depno:' + dept_no); when OTHERS then dbms_output.put_line('unkown exception'); end p_get_dept_info; function f_get_emp_num(dept_no number) return number as l_count number(2); begin select count(1) into l_count from emp where deptno=dept_no; return l_count; exception when NO_DATA_FOUND then dbms_output.put_line('invalid depno:' + dept_no); when OTHERS then dbms_output.put_line('unkown exception'); end f_get_emp_num; end emp_pkg; -----------触发器 吕晓东 17:24:58 (多人发送) 实验: 1、drtest用户拥有了scott.emp/dept表,在创建一个新的统计表 create table sta( deptno number(2), ---部门编号 emp_count number(4), --部门人员统计 primary key(deptno) ); / 2、在emp表中建立行级触发器,当向emp插入数据时,更新sta表中相应的统计结果 create or replace trigger tr_emp_insert before insert on emp for each row declare deptno_new dept.deptno%type; count_curr number(2); begin deptno_new := :new.deptno; dbms_output.put_line('新插入数据的deptno=' || to_char(deptno_new)); --判断deptno_new的值在sta表中是否存在,若存在就更新,否则就插入一条记录 select count(1) into count_curr from sta where deptno = deptno_new; dbms_output.put_line('count_curr=' || to_char(count_curr)); if count_curr <= 0 then dbms_output.put_line('向sta表插入数据'); insert into sta values(deptno_new, 1); else dbms_output.put_line('更新sta表统计数据'); update sta set emp_count = emp_count + 1 where deptno = deptno_new; end if; exception when others then dbms_output.put_line('触发器失败'); end; / 3、测试:向emp表插入一条记录 insert into emp(empno, ename, deptno) values(8006, 'ccc', 10); commit; 然后查看sta表发现增加了一条数据,当多次向统计表中已存在的部门添加人员时,则发生更新。 |