PL/SQL基础与使用

一使用PL/SQL打印hello world

笔记教程见:https://github.com/caojx-git/learn/blob/master/notes/oracle/PLSQL.sql

注意:dbms_output.put_line是oracle为我们提供的程序包,oracle中提供了很多程序包,可以自己去查相关资料

可以sqlplus中使用:desc dbms_output查看程序包的结构

二、什么是PL/SQL程序

2.1PL/SQL(procedure Language/SQL)

PL/SQL是Oracle对sql语言的过程话扩展
只在sql命令语言中增加 过程化处理(如分支,循环),使用SQL语言具有过程处理能力
PL/SQL通过将SQL语言的数据操纵能力与过程语言的数据处理能力结合起来使得SQL具有面向过程语言的扩张

2.2不同的数据库的SQL扩展

oracle:PL/SQL
DB2:SQL/PL
SQL Server:Transac-SQL(T-SQL)

三、PL/SQL的程序结构

3.1完整的PL/SQL结构块

注意:下边是PL/SQL块,与存储过程存储函数还是有一定的区别,PL/SQL块,是存储过程和存储函数的基础。

declare
说明部分(变量说明,光标申明,例外说明)
begin
语句序列(DML语句)
exception
例外处理语句
end;


注意:declare 和exception如果没有可以不写是非必需的,但是必需有 begin 和end

3.2说明部分:

3.2.1定义基本变量

类型:char ,varchar2,date,number,boolean,long
举例:
var1 char(15);
married boolean :=ture; --声明的同时初始化 PL/SQL中赋值使用 :=
pasl number(7,2);


案例:

3.2.2两种特殊变量 引用类型变量和记录类型 变量

引用型变量主要是用于记录某一 列的类型,记录型变量相当于记录了一行的变量类型,下边我们看一下两者的使用

 

引用型变量:

my_name emp.ename%type
表示变量 my_name 的类型为表emp中列ename的类型一样
举例:

 


 
 
  1. declare
  2. --定义引用型变量
  3. pename emp.ename% type;
  4. psal emp.sal%type;
  5. begin
  6. --得到员工7839的姓名和薪水
  7. select ename,sal into pename,psal from emp where empno= 7839;
  8. dbms_output.put_line('员工姓名:'||pename);
  9. dbms_output.put_line('员工薪水:'||psal);
  10. end;


注意:1.PLSQL中赋值有两种方式,第一种为 :=  第二种使用into 关键字注意,into后边的变量需要与前边的查询结果顺序一致
      2.select ename,sal into pename,psal from emp where empno=7839; 后边的赋值不用:= 与变量的赋值有区别


记录型变量:
emp_rec emp%rowtype
emp_rec表示记录了表emp一行的类型,可以认为是一个数组记录了一行中的每一列
举例:


 
 
  1. declare
  2. --定义记录型变量
  3. emp_rec emp%rowtype;
  4. begin
  5. --得到员工7839的姓名和薪水
  6. select * into emp_rec from emp where empno= 7839;
  7. dbms_output.put_line('员工姓名:'||emp_rec.ename);
  8. dbms_output.put_line('员工薪水:'||emp_rec.sal);
  9. end;

3.3程序中的语句体

PL/SQL和其他的过程语言一样,具有常见的循环分支结构,下边我们将介绍这些常见的循环分支结构在PL/SQL中的使用

3.3.1if语句

语法:
1.


 
 
  1. if 条件 then
  2. 语句1;
  3. end if;


2. 


 
 
  1. if 条件 then
  2. 语句1;
  3. else
  4. 语句2;
  5. end if;

 

3.下边这种形式主要需要注意elsif的写法,不是elseif


 
 
  1. if 条件 then
  2. 语句1;
  3. elsif 条件 then
  4. 语句2;
  5. ...
  6. elsif
  7. 语句..;
  8. end if;

 

案例:接受一个用户从键盘输入的数字,判断数字


 
 
  1. /*
  2. 判断用户从键盘输入的数字
  3. 1.接受键盘输入
  4. 2.如何使用if语句
  5. */
  6. set serveroutput on;
  7. --接受一个键盘输入
  8. --num是一个地址只,在该地址中保存输入的值
  9. accept num prompt '请输入一个数字';
  10. declare
  11. --定义个变量保存键盘输入的数字
  12. pnum number := & num;
  13. begin
  14. --执行条件判断语句
  15. if pnum = 0 then
  16. dbms_output.put_line( '你输入的数字是0');
  17. elsif pnum = 1 then
  18. dbms_output.put_line('你输入的数字是1');
  19. else
  20. dbms_output.put_line('其他数字');
  21. end if;
  22. end;


 

 

3.3.2 循环语句

1.while循环

 

while total <= 2500 loop
...
total :=total +10;
end loop;
 

案例:while打印1。。10


 
 
  1. declare
  2. --定义循环变量
  3. pnum number := 1;
  4. begin
  5. while pnum <= 10 loop
  6. --执行循环体
  7. dbms_output.put_line(pnum);
  8. pnum := pnum +1;
  9. end loop;
  10. end;

 

注意:pl/sql中不能使用++、 +=、 --等运算

2.loop循环

 

loop 
exit [when 条件];
....
end loop;
 

案例:loop打印1。。10


 
 
  1. declare
  2. --定义循环变量
  3. pnum number := 1;
  4. begin
  5. loop
  6. --退出条件
  7. exit when pnum > 10;
  8. --没有达到退出条件打印
  9. dbms_output.put_line(pnum);
  10. pnum := pnum +1;
  11. end loop;
  12. end;

 

3.for循环


for i in 1..10 loop
语句序列
end loop;


案例:使用for循环打印1。。10


 
 
  1. declare
  2. --定义循环变量
  3. pnum number := 1;
  4. begin
  5. -- 1..10表示每次取出一个给pnum变量
  6. for pnum in 1. .10 loop
  7. dbms_output.put_line(pnum);
  8. end loop;
  9. end;

3.2.3 CASE语句

case语句应用《精通Oracle10g SQL和PL/SQL》 原文

 


3.4光标的引入

光标就是一个结果集合Result Set
语法:

CURSOR 光标名称[(参数名 数据类型[参数名 数据类型]...)]
is select 语句;

3.4.1光标的属性

%found光标可以取到数据 

%notfound 光标不能取到数据
%isopen 判断光标是否打开
%rowcount 影响行数,比如说我们光标中总共有100条记录,我取走10条记录,那么%rowcount应该是10而不是100
光标的限制 

默认情况下 oracle数据库只允许在同一个回话中打开300个光标
这些信息我们可以在数据库的管理员用户中查询到

通过管理员用户可以执行如下命令


 
 
  1. show parameter abcd --语句相当于做了模糊查询
  2. show parameter cursor --查询光标的相关参数
  3. cursor_sharing string
  4. EXACT
  5. cursor_space_for_time boolean
  6. FALSE
  7. open_cursors integer
  8. 300
  9. session_cached_cursors integer
  10. 20

 

修改默认可以打开的光标数

通过管理员用户修改默认光标的打开数量:
alter system set open_cursors=4000 scope = both;
scope的取值:both,memory(表示只更改当前实例,不更改参数文件),spfile(只更改参数文件,不更改当前实例,需要重新启动数据生效)

3.4.2不带参数的光标

案例:查询并打印员工的薪水


 
 
  1. /*
  2. 1.光标的属性 4个 都是 %开头
  3. %found-取到记录 %notfound-没有取到记录
  4. %isopen 判断光标是否打开
  5. %rowcount 影响行数,比如说我们光标中总共有100条记录,我取走10条记录,那么%rowcount应该是10而不是100
  6. */
  7. declare
  8. --定义一个光标,可以不带参数
  9. cursor cemp is select ename,sal from emp;
  10. --定义变量
  11. pename emp.ename%type;
  12. psal emp.sal%type;
  13. begin
  14. --打开光标 --关闭使用之前需要打开,使用完了需要关闭
  15. open cemp;
  16. --判断光标是否打开
  17. if cemp%isopen then
  18. dbms_output.put_line('光标打开了');
  19. --循环从光标结果集合中取出数据
  20. loop
  21. --取一条记录,使用fetch从光标中取出一条记录,之后光标会向后移动,取出的值对应光标中select结果顺序值
  22. fetch cemp into pename,psal;
  23. --循环退出没有取到记录的时候
  24. exit when cemp%notfound;
  25. --否则打印记录
  26. dbms_output.put_line(pename||'薪水是'||psal);
  27. --打印影响行数
  28. dbms_output.put_line(cemp%rowcount);
  29. end loop;
  30. else
  31. dbms_output.put_line('光标没有打开');
  32. --关闭光标
  33. close cemp;
  34. end if;
  35. end;

3.4.3带参数的光标

定义的时候
与不带参数的光标的区别就是定义的时候,可以设定形参和实参
打开的时候
带参数的光标打开的时候需要传递实参


 
 
  1. declare
  2. --定义带参数的光标 括号中dno作为形参 =dno 作为实参
  3. cursor cemp(dno number) is select ename from emp where deptno=dno;
  4. --定义变量
  5. pename emp.ename%type;
  6. begin
  7. --打开光标的时候需要传递一个实参 比如说需要查询10号部门的员工姓名
  8. open cemp( 10);
  9. loop
  10. --取出每个员工的姓名
  11. fetch cemp into pename;
  12. --没有取到记录退出循环
  13. exit when cemp%notfoun;
  14. --否则打印员工姓名
  15. dbms_output.put_line(pename);
  16. end loop;
  17. --关闭光标
  18. close cemp;
  19. end;

3.5例外,异常处理

oracle中有两种例外
1.internally defined (系统定义好的例外)
比如:  

No_data_found 没有找到数据
Too_many_rows (select..into 语句匹配多个行)
Zero_Divide(被除零)
Value_error(算术或转换错误)
Timeout_on_resource(在等待资源时发生超时)
比如说在分布式数据库中  一个数据库在北京    一个数据库在上海  北京的数据库想访问上海的数据库网络断了,很久都没有等到上海的数据
给我返回结果。就会发生这种情况
....
2.user defined(自定义例外)

3.5.1系统例外

--系统例外 没有返现数据 no_data_found


 
 
  1. declare
  2. pname emp.ename% type;
  3. begin
  4. --查询员工工号是1234的员工的姓名(假如不存在该员工就会找不到数据)
  5. select ename into pename from emp where empno = 1234;
  6. exception
  7. when no_data_found then
  8. dbms_output.put_line('没有找到该员工');
  9. when others then --除了上边这个数据库中情况,都由这里捕获,避免将例外抛给数据库
  10. dbms_output.put_line('其他例外');
  11. end;

 

结果:没有找到该员工


--系统例外 返回多个结果 Too_many_rows

 


 
 
  1. declare
  2. pname emp.ename% type;
  3. begin
  4. --查询所有10号部分的员工的姓名
  5. select ename into pename from emp where deptno = 10;
  6. exception
  7. when too_many_rows then
  8. dbms_output.put_line('匹配了多个行');
  9. when others then --除了上边这个数据库中情况,都由这里捕获,避免将例外抛给数据库
  10. dbms_output.put_line('其他例外');
  11. end

结果:匹配了多个行


--系统例外 被零除 Zero_Divide


 
 
  1. declare
  2. --定义一个基本变量
  3. pnum number;
  4. begin
  5. pnum := 1/ 0;
  6. exception
  7. when zero_divide then
  8. dbms_output.put_line('0不能做除数');
  9. when others then --除了上边这个数据库中情况,都由这里捕获,避免将例外抛给数据库
  10. dbms_output.put_line('其他例外');
  11. end;

结果:0不能做除数

--系统例外 算术或转换错误 value_error


 
 
  1. declare
  2. --定义一个基本变量
  3. pnum number;
  4. begin
  5. pnum := 'abc';
  6. exception
  7. when value_error then
  8. dbms_output.put_line('算术或转换错误');
  9. when others then --除了上边这个数据库中情况,都由这里捕获,避免将例外抛给数据库
  10. dbms_output.put_line('其他例外');
  11. end;

结果:算术或转换错误

3.5.2自定义例外

除了使用系统定义好例外之外,我们还可以使用自定义例外

定义例外变量,类型是exception 
使用raise抛出自定义例外

案例:


 
 
  1. declare
  2. my_job char( 20);
  3. v_sal emp.sal%type;
  4. --自定义异常
  5. no_data exception;
  6. cursor c1 is select distinct job from emp order by job;
  7. begin
  8. open c1;
  9. fetch c1 into v_job;
  10. if c1%notfound then
  11. --抛出自定义异常
  12. raise no_data;
  13. end if;
  14. exception
  15. when no_data then --捕获自定义异常
  16. dbms_output.put_line('没有发现数据');
  17. when others then --除了上边这个数据库中情况,都由这里捕获,避免将例外抛给数据库
  18. dbms_output.put_line('其他例外');
  19. end;

 

4.0一个比较复杂的PL/SQL块

实现continue

1.说明
1.下边的for emm in (select * from newqdgl.agent_point_not_user_sql) loop .. end loop; 中for循环将查询结果

集合保存到emm临时变量中,这很像光标cursor的功能


2.oracle的循环语句在较低的版本中有break表示结束整个循环,不过没有continue跳过本次循环的功能,我们可以通过
goto语句间接的实现continue功能 如下定义命名块<<endlabel>> ,使用goto endlabel;就可以跳转到有名块


3.有名块和匿名块

前边我们用的都是匿名块,定义有名块语法 <<名字>>


 
 
  1. declare
  2. v_OpId number( 12);
  3. v_Org_Id number(12);
  4. v_llCountMonth number(12); --表示本月需要积分出账的月份
  5. v_iChangePoint number(12); --表示转出积分
  6. v_pointsSysId number(12); --积分编号
  7. v_lPointsValue number(12); --转出积分临时变量
  8. v_sql varchar2(1024);
  9. v_thisyearValuedPoints number(12); --积分子表年度积分
  10. v_CurrPoints number(12); --积分子表当前积分
  11. v_docode number(12); --获取业务记录
  12. v_doneCode number(12);
  13. v_subdtail_CurrPoints number(12); --积分明细当前积分
  14. v_POINTS_INFO_count number(2);
  15. begin
  16. v_OpId := 999;
  17. v_Org_Id := 999;
  18. v_llCountMonth := 201608;
  19. v_iChangePoint := 0;
  20. v_pointsSysId := 0;
  21. v_lPointsValue := 0;
  22. v_subdtail_CurrPoints := 0;
  23. v_POINTS_INFO_count :=0;
  24. -- 通过下边这种for循环的方式,可以将查询结果集合保存到一个临时集合变量中,可以达到类似于光标cursor的效果
  25. for emm in ( select * from newqdgl.agent_point_not_user_sql) loop
  26. dbms_output.put_line( '1---' || emm.agent_id || '--' ||emm.curr_point_not_exc);
  27. -- 3.积分变更
  28. --根据代理商编号查询对应积分分表编号
  29. select count( 1) into v_POINTS_INFO_count from
  30. newqdgl.POINTS_INFO
  31. WHERE 1 = 1
  32. AND POINTS_ID = emm.agent_id;
  33. if v_POINTS_INFO_count =0 then
  34. goto endlabel;
  35. end if;
  36. SELECT POINTS_SYS_ID
  37. into v_pointsSysId
  38. FROM newqdgl.POINTS_INFO
  39. WHERE 1 = 1
  40. AND POINTS_ID = emm.agent_id;
  41. dbms_output.put_line('2 ---'||v_pointsSysId);
  42. -- 3.1 计算 不可兑换转出积分 == 可兑换新增积分
  43. if mod(v_llCountMonth, 100) = 1 then
  44. v_iChangePoint := emm.curr_point_not_exc;
  45. else
  46. v_iChangePoint := emm.curr_point_not_exc /
  47. (14 - mod(v_llCountMonth, 100));
  48. end if;
  49. dbms_output.put_line('3 ----'||v_pointsSysId||'---'||v_iChangePoint);
  50. --3.2更新AGENT_POINT_NOT_USER 表中的不可兑换积分
  51. UPDATE newqdgl.AGENT_POINT_NOT_USER
  52. set CURR_POINT_NOT_EXC =
  53. (emm.curr_point_not_exc - v_iChangePoint),
  54. REC_STATUS = 1
  55. WHERE AGENT_ID = emm.agent_id;
  56. --3.3 不考虑赤字积分
  57. v_lPointsValue := v_iChangePoint;
  58. --3.4新增积分addAgentCurrPoint(pointsSysId, agentId, lPointsValue, llCountMonth);
  59. dbms_output.put_line('积分新增开始' || v_pointsSysId || ' ---' ||emm.agent_id);
  60. --3.4.1查询积分子表中的信息
  61. SELECT CURR_POINTS, THISYEAR_VALUED_POINTS
  62. into v_CurrPoints, v_thisyearValuedPoints
  63. FROM newqdgl.ods_points_subinfo
  64. WHERE 1 = 1
  65. AND POINTS_SYS_ID = v_pointsSysId
  66. AND POINTS_ID = emm.agent_id;
  67. dbms_output.put_line('4 ----');
  68. -- 2.4.2插入积分子表记录到临时表
  69. insert into ods_points_subinfo_temp
  70. (POINTS_SYS_ID,
  71. POINTS_ID_TYPE,
  72. POINTS_ID,
  73. CURR_POINTS,
  74. THISYEAR_VALUED_POINTS)
  75. values
  76. (v_pointsSysId,
  77. 4,
  78. emm.agent_id,
  79. (v_CurrPoints + v_lPointsValue),
  80. (v_thisyearValuedPoints + v_lPointsValue));
  81. dbms_output.put_line('5 ----');
  82. --3.4.3查询积分明细
  83. SELECT POINTS_SYS_ID SUB_CURR_POINTS
  84. into v_subdtail_CurrPoints
  85. FROM points_info_detail_info
  86. WHERE 1 = 1
  87. AND POINTS_ID = emm.agent_id
  88. AND POINTS_ID_TYPE = 4
  89. AND POINTS_SYS_ID = v_pointsSysId
  90. AND POINTS_SUB_TYPE = 1;
  91. dbms_output.put_line('6 ----');
  92. --3.4.4插入积分明细到临时表
  93. insert into points_info_detail_info_temp
  94. (POINTS_SYS_ID,
  95. POINTS_ID_TYPE,
  96. POINTS_ID,
  97. POINTS_SUB_TYPE,
  98. SUB_CURR_POINTS)
  99. values
  100. (v_pointsSysId,
  101. 4,
  102. emm.agent_id,
  103. 1,
  104. (v_subdtail_CurrPoints + v_lPointsValue));
  105. dbms_output.put_line('7 ----');
  106. --3.4.5 获取业务记录序列
  107. SELECT newqdgl.SEQ_DONE_NEW_CODE.NEXTVAL into v_docode FROM DUAL;
  108. INSERT INTO points_subcount_info_temp
  109. (points_sys_id,
  110. points_id_type,
  111. points_id,
  112. get_sub_points,
  113. points_sub_type,
  114. done_code)
  115. VALUES
  116. (v_pointsSysId, 4, emm.agent_id, v_lPointsValue, 1, v_docode);
  117. dbms_output.put_line('8 ----' || v_CurrPoints || '--' ||v_thisyearValuedPoints || '--' || v_lPointsValue || '--' ||v_llCountMonth);
  118. insert into ods_points_count_temp
  119. (points_sys_id,
  120. points_id_type,
  121. points_id,
  122. get_points,
  123. points_busi_code,
  124. done_date,
  125. done_code,
  126. bill_month,
  127. op_id,
  128. curr_points,
  129. thisyear_valued_points,
  130. ext4)
  131. values
  132. (v_pointsSysId,
  133. 4,
  134. emm.agent_id,
  135. v_lPointsValue,
  136. 19,
  137. to_date( '20160805', 'yyyymmdd'),
  138. v_docode,
  139. v_llCountMonth,
  140. 9,
  141. (v_CurrPoints + v_lPointsValue),
  142. (v_thisyearValuedPoints + v_lPointsValue),
  143. to_char(v_CurrPoints + v_lPointsValue));
  144. dbms_output.put_line('积分新增结束' || v_pointsSysId || ' ---' ||emm.agent_id);
  145. select newqdgl.SEQ_DONE_CODE.nextval into v_doneCode from dual;
  146. --插入积分记录
  147. insert into newqdgl.Channel_Point_Record_Ext
  148. (Chanenel_Entity_Id,
  149. Operate_Code,
  150. Oper_Value,
  151. Done_Code,
  152. Done_Date,
  153. Org_Id,
  154. Op_Id)
  155. values
  156. (emm.agent_id,
  157. 8020311,
  158. 0 - v_iChangePoint,
  159. v_doneCode,
  160. to_date( '20160805', 'YYYY/MM/DD'), --可以写成具体的出账日期
  161. v_Org_Id,
  162. v_OpId);
  163. dbms_output.put_line('9 ----');
  164. insert into newqdgl.Channel_Point_Record_Ext
  165. (Chanenel_Entity_Id,
  166. Operate_Code,
  167. Oper_Value,
  168. Done_Code,
  169. Done_Date,
  170. Org_Id,
  171. Op_Id)
  172. values
  173. (emm.agent_id,
  174. 8020310,
  175. v_iChangePoint,
  176. v_doneCode,
  177. to_date( '20160805', 'YYYY/MM/DD'), ----可以写成具体的出账日期
  178. v_Org_Id,
  179. v_OpId);
  180. dbms_output.put_line('10 ----');
  181. <<endlabel>> --这里相当于定义了一个有名的的块,endlabel ,使用goto endlabel 可以跳过goto endlabel 到endlabel之间的代码
  182. --即跳到for循环的最后,便相当于实现了continue,跳到这里后,不干什么需要些null;
  183. null;
  184. end loop;
  185. --commit;
  186. EXCEPTION
  187. WHEN OTHERS THEN
  188. ROLLBACK;
  189. dbms_output.put_line('error');
  190. RETURN;
  191. end;

2.动态的拼接出sql

使用 execute immediate v_sql; 可以立即执行拼接后的sql,同时我们也可以使用输出语句将生成后的sql打印出来。

 


 
 
  1. // ------------------3.切换用户到"aicbs" 将临时表中的数据插入到各个分表--------------------------
  2. -- 1. ods_points_count_temp
  3. declare
  4. v_sql varchar2( 1024);
  5. begin
  6. for i in 0 .. 9 loop
  7. v_sql:= 'insert into aicbs.points_count0'||i|| '_2016 select * from newqdgl.ods_points_count_temp where mod(points_sys_id,10) = '||i;
  8. execute immediate v_sql;
  9. end loop;
  10. --commit;
  11. EXCEPTION
  12. WHEN OTHERS THEN
  13. ROLLBACK;
  14. dbms_output.put_line('error');
  15. RETURN;
  16. end;


2.1如果涉及到日期相关的使用‘’转移成一个‘


 
 
  1. create table test(
  2. done_date date
  3. );
  4. declare
  5. v_sql varchar2( 1024);
  6. begin
  7. for i in 0 .. 9 loop
  8. v_sql:= 'insert into test(done_date) values(to_date(''2016/10/30'',''yyyy/mm/dd''))';
  9. execute immediate v_sql;
  10. end loop;
  11. commit;
  12. EXCEPTION
  13. WHEN OTHERS THEN
  14. ROLLBACK;
  15. dbms_output.put_line('error');
  16. RETURN;
  17. end;

 

结果:

 

原文:

https://github.com/caojx-git/learn/blob/master/notes/oracle/PLSQL.sql

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值