Oracle中游标Cursor基本用法详解
查询
SELECT语句用于从数据库中查询数据,当在PL/SQL中使用SELECT语句时,要与INTO子句一起使用,查询的
返回值被赋予INTO子句中的变量,变量的声明是在DELCARE中。SELECTINTO语法如下:
SELECT[DISTICT|ALL]{*|column[,column,...]} INTO(variable[,variable,...]|record) FROM{table|(sub-query)}[alias] WHERE............
PL/SQL中SELECT语句只返回一行数据。如果超过一行数据,那么就要使用显式游标(对游标的讨论我们将
在后面进行),INTO子句中要有与SELECT子句中相同列数量的变量。INTO子句中也可以是记录变量。
%TYPE属性
在PL/SQL中可以将变量和常量声明为内建或用户定义的数据类型,以引用一个列名,同时继承他的数据类
型和大小。这种动态赋值方法是非常有用的,比如变量引用的列的数据类型和大小改变了,如果使用了%TYPE,
那么用户就不必修改代码,否则就必须修改代码。
例:
v_empnoSCOTT.EMP.EMPNO%TYPE; v_salaryEMP.SALARY%TYPE;
不但列名可以使用%TYPE,而且变量、游标、记录,或声明的常量都可以使用%TYPE。这对于定义相同数据类
型的变量非常有用。
DELCARE V_ANUMBER(5):=10; V_BV_A%TYPE:=15; V_CV_A%TYPE; BEGIN DBMS_OUTPUT.PUT_LINE ('V_A='||V_A||'V_B='||V_B||'V_C='||V_C); END SQL>/ V_A=10V_B=15V_C= PL/SQLproceduresuccessfullycompleted. SQL>
其他DML语句
其它操作数据的DML语句是:INSERT、UPDATE、DELETE和LOCKTABLE,这些语句在PL/SQL中的语法与在SQL中
的语法相同。我们在前面已经讨
论过DML语句的使用这里就不再重复了。在DML语句中可以使用任何在DECLARE部分声明的变量,如果是嵌套
块,那么要注意变量的作用范围。
例:
CREATEORREPLACEPROCEDUREFIRE_EMPLOYEE(pempnoinnumber) AS v_enameEMP.ENAME%TYPE; BEGIN SELECTenameINTOv_ename FROMemp WHEREempno=p_empno; INSERTINTOFORMER_EMP(EMPNO,ENAME) VALUES(p_empno,v_ename); DELETEFROMemp WHEREempno=p_empno; UPDATEformer_emp SETdate_deleted=SYSDATE WHEREempno=p_empno; EXCEPTION WHENNO_DATA_FOUNDTHEN DBMS_OUTPUT.PUT_LINE('EmployeeNumberNotFound!'); END
DML语句的结果
当执行一条DML语句后,DML语句的结果保存在四个游标属性中,这些属性用于控制程序流程或者了解程序
的状态。当运行DML语句时,PL/SQL打开一个内建游标并处理结果,游标是维护查询结果的内存中的一个区域,
游标在运行DML语句时打开,完成后关闭。隐式游标只使用SQL%FOUND,
SQL%NOTFOUND,SQL%ROWCOUNT三个属性.SQL%FOUND,SQL%NOTFOUND是布尔值,SQL%ROWCOUNT是整数值。
SQL%FOUND和SQL%NOTFOUND
在执行任何DML语句前SQL%FOUND和SQL%NOTFOUND的值都是NULL,在执行DML语句后,SQL%FOUND的属性值将是:
.TRUE:INSERT
.TRUE:DELETE和UPDATE,至少有一行被DELETE或UPDATE.
.TRUE:SELECTINTO至少返回一行
当SQL%FOUND为TRUE时,SQL%NOTFOUND为FALSE。
SQL%ROWCOUNT
在执行任何DML语句之前,SQL%ROWCOUNT的值都是NULL,对于SELECTINTO语句,如果执行成功
,SQL%ROWCOUNT的值为1,如果没有成功,SQL%ROWCOUNT的值为0,同时产生一个异常NO_DATA_FOUND.
SQL%ISOPEN
SQL%ISOPEN是一个布尔值,如果游标打开,则为TRUE,如果游标关闭,则为FALSE.对于隐式游标而言SQL%
ISOPEN总是FALSE,这是因为隐式游
标在DML语句执行时打开,结束时就立即关闭。
事务控制语句
事务是一个工作的逻辑单元可以包括一个或多个DML语句,事物控制帮助用户保证数据的一致性。如果事务控制逻辑单元中的任何一个DML
语句失败,那么整个事务都将回滚,在PL/SQL中用户可以明确地使用COMMIT、ROLLBACK、SAVEPOINT以及
SETTRANSACTION语句。
COMMIT语句终止事务,永久保存数据库的变化,同时释放所有LOCK,ROLLBACK终止现行事务释放所有LOCK,
但不保存数据库的任何变化,SAVEPOI
NT用于设置中间点,当事务调用过多的数据库操作时,中间点是非常有用的,SETTRANSACTION用于设置事
务属性,比如read-write和隔离级等。
显式游标
查询返回结果超过一行时,就需要一个显式游标,此时用户不能使用selectinto语句。PL/SQL管理隐式
游标,当查询开始时隐式游标打开,查询结束时隐式游标自动关闭。显式游标在PL/SQL块的声明部分声明,在执行部分或异常处理部分打开,取数据,关闭。
使用游标
这里要做一个声明,我们所说的游标通常是指显式游标,因此从现在起没有特别指明的情况,我们所说的
游标都是指显式游标。要在程序中使用游标,必须首先声明游标。
声明游标
语法:
CURSORcursor_nameISselect_statement;
在PL/SQL中游标名是一个未声明变量,不能给游标名赋值或用于表达式中。
例:
DELCARE CURSORC_EMPISSELECTempno,ename,salary FROMemp WHEREsalary>2000 ORDERBYename; ........ BEGIN
在游标定义中SELECT语句中不一定非要表可以是视图,也可以从多个表或视图中选择的列,甚至可以使用*
来选择所有的列。
打开游标
使用游标中的值之前应该首先打开游标,打开游标初始化查询处理。打开游标的语法是:
OPENcursor_name
cursor_name是在声明部分定义的游标名。
例:
OPENC_EMP;
关闭游标
语法:
CLOSEcursor_name
例:
CLOSEC_EMP;
从游标提取数据
从游标得到一行数据使用FETCH命令。每一次提取数据后,游标都指向结果集的下一行。语法如下:
FETCHcursor_nameINTOvariable[,variable,...]
对于SELECT定义的游标的每一列,FETCH变量列表都应该有一个变量与之相对应,变量的类型也要相同。
例:
SETSERVERIUTPUTON DECLARE v_enameEMP.ENAME%TYPE; v_salaryEMP.SALARY%TYPE; CURSORc_empISSELECTename,salaryFROMemp; BEGIN OPENc_emp; FETCHc_empINTOv_ename,v_salary; DBMS_OUTPUT.PUT_LINE('SalaryofEmployee'||v_ename||'is'||v_salary); FETCHc_empINTOv_ename,v_salary; DBMS_OUTPUT.PUT_LINE('SalaryofEmployee'||v_ename||'is'||v_salary); FETCHc_empINTOv_ename,v_salary; DBMS_OUTPUT.PUT_LINE('SalaryofEmployee'||v_ename||'is'||v_salary); CLOSEc_emp; END
这段代码无疑是非常麻烦的,如果有多行返回结果,可以使用循环并用游标属性为结束循环的条件,以这
种方式提取数据,程序的可读性和简洁性都大为提高,下面我们使用循环重新写上面的程序:
SETSERVERIUTPUTON DECLARE v_enameEMP.ENAME%TYPE; v_salaryEMP.SALARY%TYPE; CURSORc_empISSELECTename,salaryFROMemp; BEGIN OPENc_emp; LOOP FETCHc_empINTOv_ename,v_salary; EXITWHENc_emp%NOTFOUND; DBMS_OUTPUT.PUT_LINE('SalaryofEmployee'||v_ename||'is'||v_salary); END
记录变量
定义一个记录变量使用TYPE命令和%ROWTYPE,关于%ROWsTYPE的更多信息请参阅相关资料。
记录变量用于从游标中提取数据行,当游标选择很多列的时候,那么使用记录比为每列声明一个变量要方
便得多。
当在表上使用%ROWTYPE并将从游标中取出的值放入记录中时,如果要选择表中所有列,那么在SELECT子句
中使用*比将所有列名列出来要安全得多。
例:
SETSERVERIUTPUTON DECLARE R_empEMP%ROWTYPE; CURSORc_empISSELECT*FROMemp; BEGIN OPENc_emp; LOOP FETCHc_empINTOr_emp; EXITWHENc_emp%NOTFOUND; DBMS_OUT.PUT.PUT_LINE('SalaryofEmployee'||r_emp.ename||'is'||r_emp.salary); ENDLOOP; CLOSEc_emp; END;
%ROWTYPE也可以用游标名来定义,这样的话就必须要首先声明游标:
SETSERVERIUTPUTON DECLARE CURSORc_empISSELECTename,salaryFROMemp; R_empc_emp%ROWTYPE; BEGIN OPENc_emp; LOOP FETCHc_empINTOr_emp; EXITWHENc_emp%NOTFOUND; DBMS_OUT.PUT.PUT_LINE('SalaryofEmployee'||r_emp.ename||'is'||r_emp.salary); ENDLOOP; CLOSEc_emp; END;
带参数的游标
与存储过程和函数相似,可以将参数传递给游标并在查询中使用。这对于处理在某种条件下打开游标的情
况非常有用。它的语法如下:
CURSORcursor_name[(parameter[,parameter],...)]ISselect_statement;
定义参数的语法如下:
Parameter_name[IN]data_type[{:=|DEFAULT}value]
与存储过程不同的是,游标只能接受传递的值,而不能返回值。参数只定义数据类型,没有大小。
另外可以给参数设定一个缺省值,当没有参数值传递给游标时,就使用缺省值。游标中定义的参数只是一个占位符,在别处引用该参数不一定可靠。
在打开游标时给参数赋值,语法如下:
OPENcursor_name[value[,value]....];
参数值可以是文字或变量。
例:
DECALRE CURSORc_deptISSELECT*FROMdeptORDERBYdeptno; CURSORc_emp(p_deptVARACHAR2)IS SELECTename,salary FROMemp WHEREdeptno=p_dept ORDERBYename r_deptDEPT%ROWTYPE; v_enameEMP.ENAME%TYPE; v_salaryEMP.SALARY%TYPE; v_tot_salaryEMP.SALARY%TYPE; BEGIN OPENc_dept; LOOP FETCHc_deptINTOr_dept; EXITWHENc_dept%NOTFOUND; DBMS_OUTPUT.PUT_LINE('Department:'||r_dept.deptno||'-'||r_dept.dname); v_tot_salary:=0; OPENc_emp(r_dept.deptno); LOOP FETCHc_empINTOv_ename,v_salary; EXITWHENc_emp%NOTFOUND; DBMS_OUTPUT.PUT_LINE('Name:'||v_ename||'salary:'||v_salary); v_tot_salary:=v_tot_salary+v_salary; ENDLOOP; CLOSEc_emp; DBMS_OUTPUT.PUT_LINE('ToltalSalaryfordept:'||v_tot_salary); ENDLOOP; CLOSEc_dept; END;
游标FOR循环
在大多数时候我们在设计程序的时候都遵循下面的步骤:
1、打开游标
2、开始循环
3、从游标中取值
4、检查那一行被返回
5、处理
6、关闭循环
7、关闭游标
可以简单的把这一类代码称为游标用于循环。但还有一种循环与这种类型不相同,这就是FOR循环,用于
FOR循环的游标按照正常的声明方式声明,它的优点在于不需要显式的打开、关闭、取数据,测试数据的存在、定义存放数据的变量等等。
游标FOR循环的语法如下:
FORrecord_nameIN (corsor_name[(parameter[,parameter]...)] |(query_difinition) LOOP statements ENDLOOP;
下面我们用for循环重写上面的例子:
DECALRE CURSORc_deptISSELECTdeptno,dnameFROMdeptORDERBYdeptno; CURSORc_emp(p_deptVARACHAR2)IS SELECTename,salary FROMemp WHEREdeptno=p_dept ORDERBYename v_tot_salaryEMP.SALARY%TYPE; BEGIN FORr_deptINc_deptLOOP DBMS_OUTPUT.PUT_LINE('Department:'||r_dept.deptno||'-'||r_dept.dname); v_tot_salary:=0; FORr_empINc_emp(r_dept.deptno)LOOP DBMS_OUTPUT.PUT_LINE('Name:'||v_ename||'salary:'||v_salary); v_tot_salary:=v_tot_salary+v_salary; ENDLOOP; DBMS_OUTPUT.PUT_LINE('ToltalSalaryfordept:'||v_tot_salary); ENDLOOP; END;
在游标FOR循环中使用查询
在游标FOR循环中可以定义查询,由于没有显式声明所以游标没有名字,记录名通过游标查询来定义。
DECALRE v_tot_salaryEMP.SALARY%TYPE; BEGIN FORr_deptIN(SELECTdeptno,dnameFROMdeptORDERBYdeptno)LOOP DBMS_OUTPUT.PUT_LINE('Department:'||r_dept.deptno||'-'||r_dept.dname); v_tot_salary:=0; FORr_empIN(SELECTename,salary FROMemp WHEREdeptno=p_dept ORDERBYename)LOOP DBMS_OUTPUT.PUT_LINE('Name:'||v_ename||'salary:'||v_salary); v_tot_salary:=v_tot_salary+v_salary; ENDLOOP; DBMS_OUTPUT.PUT_LINE('ToltalSalaryfordept:'||v_tot_salary); ENDLOOP; END;
游标中的子查询
语法如下:
CURSORC1ISSELECT*FROMemp WHEREdeptnoNOTIN(SELECTdeptno FROMdept WHEREdname!='ACCOUNTING');
可以看出与SQL中的子查询没有什么区别。
游标中的更新和删除
在PL/SQL中依然可以使用UPDATE和DELETE语句更新或删除数据行。显式游标只有在需要获得多行数据的情
况下使用。PL/SQL提供了仅仅使用游标就可以执行删除或更新记录的方法。
UPDATE或DELETE语句中的WHERECURRENTOF子串专门处理要执行UPDATE或DELETE操作的表中取出的最近的
数据。要使用这个方法,在声明游标时必须使用FORUPDATE子串,当对话使用FORUPDATE子串打开一个游标时,所有返回集中的数据行都将处于行级(ROW-LEVEL)独占式锁定,其
他对象只能查询这些数据行,不能进行UPDATE、DELETE或SELECT...FOR UPDATE操作。
语法:
FORUPDATE[OF[schema.]table.column[,[schema.]table.column]..
[nowait]
在多表查询中,使用OF子句来锁定特定的表,如果忽略了OF子句,那么所有表中选择的数据行都将被锁定。
如果这些数据行已经被其他会话锁定,那么正常情况下ORACLE将等待,直到数据行解锁。
在UPDATE和DELETE中使用WHERECURRENTOF子串的语法如下:
WHERE{CURRENTOFcursor_name|search_condition}
例:
DELCARE CURSORc1ISSELECTempno,salary FROMemp WHEREcommISNULL FORUPDATEOFcomm; v_commNUMBER(10,2); BEGIN FORr1INc1LOOP IFr1.salary<500THEN v_comm:=r1.salary*0.25; ELSEIFr1.salary<1000THEN v_comm:=r1.salary*0.20; ELSEIFr1.salary<3000THEN v_comm:=r1.salary*0.15; ELSE v_comm:=r1.salary*0.12; ENDIF; UPDATEemp; SETcomm=v_comm WHERECURRENTOFc1l; ENDLOOP; END
-声明游标 --宗地表的调查日期LANDINFO_RESEARCHDATE --复制到流程表的权属调查时间FLOW_REASEARCHTIME DECLARE cursorcur_sel_allisselectLANDINFO_RESEARCHDATE,LANDINFO_LANDNOfromt_leoa_landinfo;--定义游标 l_datet_leoa_landinfo.landinfo_researchdate%type;--声明变量分别保存t_leoa_landinfo的各列 l_landNot_leoa_landinfo.landinfo_landno%type; begin opencur_sel_all; loop--循环取数,并将游标数据填充到返回纪录集合中 fetchcur_sel_allintol_date,l_landNo; exitwhencur_sel_all%NOTFOUND;--循环退出条件 ifcur_sel_all%FOUNDthen--获取数据 updateT_LEOA_BOOKFLOWt2setFLOW_REASEARCHTIME=l_datewherel_landNo=t2.landinfo_landno; endif; endloop; closecur_sel_all; end;
下面再分享一下另外一则游标使用方法的代码,具体如下:
--声明游标;CURSORcursor_nameISselect_statement --For循环游标 --(1)定义游标 --(2)定义游标变量 --(3)使用for循环来使用这个游标 declare --类型定义 cursorc_job is selectempno,ename,job,sal fromemp wherejob='MANAGER'; --定义一个游标变量v_cinfoc_emp%ROWTYPE,该类型为游标c_emp中的一行数据类型 c_rowc_job%rowtype; begin forc_rowinc_jobloop dbms_output.put_line(c_row.empno||'-'||c_row.ename||'-'||c_row.job||'-'||c_row.sal); endloop; end; --Fetch游标 --使用的时候必须要明确的打开和关闭 declare --类型定义 cursorc_job is selectempno,ename,job,sal fromemp wherejob='MANAGER'; --定义一个游标变量 c_rowc_job%rowtype; begin openc_job; loop --提取一行数据到c_row fetchc_jobintoc_row; --判读是否提取到值,没取到值就退出 --取到值c_job%notfound是false --取不到值c_job%notfound是true exitwhenc_job%notfound; dbms_output.put_line(c_row.empno||'-'||c_row.ename||'-'||c_row.job||'-'||c_row.sal); endloop; --关闭游标 closec_job; end; --1:任意执行一个update操作,用隐式游标sql的属性%found,%notfound,%rowcount,%isopen观察update语句的执行情况。 begin updateempsetENAME='ALEARK'WHEREEMPNO=7469; ifsql%isopenthen dbms_output.put_line('Openging'); else dbms_output.put_line('closing'); endif; ifsql%foundthen dbms_output.put_line('游标指向了有效行');--判断游标是否指向有效行 else dbms_output.put_line('Sorry'); endif; ifsql%notfoundthen dbms_output.put_line('AlsoSorry'); else dbms_output.put_line('Haha'); endif; dbms_output.put_line(sql%rowcount); exception whenno_data_foundthen dbms_output.put_line('SorryNodata'); whentoo_many_rowsthen dbms_output.put_line('TooManyrows'); end; declare empNumberemp.EMPNO%TYPE; empNameemp.ENAME%TYPE; begin ifsql%isopenthen dbms_output.put_line('Cursorisopinging'); else dbms_output.put_line('CursorisClose'); endif; ifsql%notfoundthen dbms_output.put_line('NoValue'); else dbms_output.put_line(empNumber); endif; dbms_output.put_line(sql%rowcount); dbms_output.put_line('-------------'); selectEMPNO,ENAMEintoempNumber,empNamefromempwhereEMPNO=7499; dbms_output.put_line(sql%rowcount); ifsql%isopenthen dbms_output.put_line('Cursorisopinging'); else dbms_output.put_line('CursorisClosing'); endif; ifsql%notfoundthen dbms_output.put_line('NoValue'); else dbms_output.put_line(empNumber); endif; exception whenno_data_foundthen dbms_output.put_line('NoValue'); whentoo_many_rowsthen dbms_output.put_line('toomanyrows'); end; --2,使用游标和loop循环来显示所有部门的名称 --游标声明 declare cursorcsr_dept is --select语句 selectDNAME fromDepth; --指定行指针,这句话应该是指定和csr_dept行类型相同的变量 row_deptcsr_dept%rowtype; begin --for循环 forrow_deptincsr_deptloop dbms_output.put_line('部门名称:'||row_dept.DNAME); endloop; end; --3,使用游标和while循环来显示所有部门的的地理位置(用%found属性) declare --游标声明 cursorcsr_TestWhile is --select语句 selectLOC fromDepth; --指定行指针 row_loccsr_TestWhile%rowtype; begin --打开游标 opencsr_TestWhile; --给第一行喂数据 fetchcsr_TestWhileintorow_loc; --测试是否有数据,并执行循环 whilecsr_TestWhile%foundloop dbms_output.put_line('部门地点:'||row_loc.LOC); --给下一行喂数据 fetchcsr_TestWhileintorow_loc; endloop; closecsr_TestWhile; end; select*fromemp --4,接收用户输入的部门编号,用for循环和游标,打印出此部门的所有雇员的所有信息(使用循环游标) --CURSORcursor_name[(parameter[,parameter],...)]ISselect_statement; --定义参数的语法如下:Parameter_name[IN]data_type[{:=|DEFAULT}value] declare CURSOR c_dept(p_deptNonumber) is select*fromempwhereemp.depno=p_deptNo; r_empemp%rowtype; begin forr_empinc_dept(20)loop dbms_output.put_line('员工号:'||r_emp.EMPNO||'员工名:'||r_emp.ENAME||'工资:'||r_emp.SAL); endloop; end; select*fromemp --5:向游标传递一个工种,显示此工种的所有雇员的所有信息(使用参数游标) declare cursor c_job(p_jobnvarchar2) is select*fromempwhereJOB=p_job; r_jobemp%rowtype; begin forr_jobinc_job('CLERK')loop dbms_output.put_line('员工号'||r_job.EMPNO||''||'员工姓名'||r_job.ENAME); endloop; end; SELECT*FROMEMP --6:用更新游标来为雇员加佣金:(用if实现,创建一个与emp表一摸一样的emp1表,对emp1表进行修改操作),并将更新前后的数据输出出来 --http://zheng12tian.iteye.com/blog/815770 createtableemp1asselect*fromemp; declare cursor csr_Update is select*fromemp1forupdateOFSAL; empInfocsr_Update%rowtype; saleInfoemp1.SAL%TYPE; begin FORempInfoINcsr_UpdateLOOP IFempInfo.SAL<1500THEN saleInfo:=empInfo.SAL*1.2; elsifempInfo.SAL<2000THEN saleInfo:=empInfo.SAL*1.5; elsifempInfo.SAL<3000THEN saleInfo:=empInfo.SAL*2; ENDIF; UPDATEemp1SETSAL=saleInfoWHERECURRENTOFcsr_Update; ENDLOOP; END; --7:编写一个PL/SQL程序块,对名字以‘A'或‘S'开始的所有雇员按他们的基本薪水(sal)的10%给他们加薪(对emp1表进行修改操作) declare cursor csr_AddSal is select*fromemp1whereENAMELIKE'A%'ORENAMELIKE'S%'forupdateOFSAL; r_AddSalcsr_AddSal%rowtype; saleInfoemp1.SAL%TYPE; begin forr_AddSalincsr_AddSalloop dbms_output.put_line(r_AddSal.ENAME||'原来的工资:'||r_AddSal.SAL); saleInfo:=r_AddSal.SAL*1.1; UPDATEemp1SETSAL=saleInfoWHERECURRENTOFcsr_AddSal; endloop; end; --8:编写一个PL/SQL程序块,对所有的salesman增加佣金(comm)500 declare cursor csr_AddComm(p_jobnvarchar2) is select*fromemp1whereJOB=p_jobFORUPDATEOFCOMM; r_AddCommemp1%rowtype; commInfoemp1.comm%type; begin forr_AddCommincsr_AddComm('SALESMAN')LOOP commInfo:=r_AddComm.COMM+500; UPDATEEMP1SETCOMM=commInfowhereCURRENTOFcsr_AddComm; ENDLOOP; END; --9:编写一个PL/SQL程序块,以提升2个资格最老的职员为MANAGER(工作时间越长,资格越老) --(提示:可以定义一个变量作为计数器控制游标只提取两条数据;也可以在声明游标的时候把雇员中资格最老的两个人查出来放到游标中。) declare cursorcrs_testComput is select*fromemp1orderbyHIREDATEasc; --计数器 top_twonumber:=2; r_testComputcrs_testComput%rowtype; begin opencrs_testComput; FETCHcrs_testComputINTOr_testComput; whiletop_two>0loop dbms_output.put_line('员工姓名:'||r_testComput.ENAME||'工作时间:'||r_testComput.HIREDATE); --计速器减一 top_two:=top_two-1; FETCHcrs_testComputINTOr_testComput; endloop; closecrs_testComput; end; --10:编写一个PL/SQL程序块,对所有雇员按他们的基本薪水(sal)的20%为他们加薪, --如果增加的薪水大于300就取消加薪(对emp1表进行修改操作,并将更新前后的数据输出出来) declare cursor crs_UpadateSal is select*fromemp1forupdateofSAL; r_UpdateSalcrs_UpadateSal%rowtype; salAddemp1.sal%type; salInfoemp1.sal%type; begin forr_UpdateSalincrs_UpadateSalloop salAdd:=r_UpdateSal.SAL*0.2; ifsalAdd>300then salInfo:=r_UpdateSal.SAL; dbms_output.put_line(r_UpdateSal.ENAME||':加薪失败。'||'薪水维持在:'||r_UpdateSal.SAL); else salInfo:=r_UpdateSal.SAL+salAdd; dbms_output.put_line(r_UpdateSal.ENAME||':加薪成功.'||'薪水变为:'||salInfo); endif; updateemp1setSAL=salInfowherecurrentofcrs_UpadateSal; endloop; end; --11:将每位员工工作了多少年零多少月零多少天输出出来 --近似 --CEIL(n)函数:取大于等于数值n的最小整数 --FLOOR(n)函数:取小于等于数值n的最大整数 --truc的用法http://publish.it168.com/2005/1028/20051028034101.shtml declare cursor crs_WorkDay is selectENAME,HIREDATE,trunc(months_between(sysdate,hiredate)/12)ASSPANDYEARS, trunc(mod(months_between(sysdate,hiredate),12))ASmonths, trunc(mod(mod(sysdate-hiredate,365),12))asdays fromemp1; r_WorkDaycrs_WorkDay%rowtype; begin forr_WorkDayincrs_WorkDayloop dbms_output.put_line(r_WorkDay.ENAME||'已经工作了'||r_WorkDay.SPANDYEARS||'年,零'||r_WorkDay.months||'月,零'||r_WorkDay.days||'天'); endloop; end; --12:输入部门编号,按照下列加薪比例执行(用CASE实现,创建一个emp1表,修改emp1表的数据),并将更新前后的数据输出出来 --deptnoraise(%) --105% --2010% --3015% --4020% --加薪比例以现有的sal为标准 --CASEexprWHENcomparison_exprTHENreturn_expr --[,WHENcomparison_exprTHENreturn_expr]...[ELSEelse_expr]END declare cursor crs_caseTest is select*fromemp1forupdateofSAL; r_caseTestcrs_caseTest%rowtype; salInfoemp1.sal%type; begin forr_caseTestincrs_caseTestloop case whenr_caseTest.DEPNO=10 THENsalInfo:=r_caseTest.SAL*1.05; whenr_caseTest.DEPNO=20 THENsalInfo:=r_caseTest.SAL*1.1; whenr_caseTest.DEPNO=30 THENsalInfo:=r_caseTest.SAL*1.15; whenr_caseTest.DEPNO=40 THENsalInfo:=r_caseTest.SAL*1.2; endcase; updateemp1setSAL=salInfowherecurrentofcrs_caseTest; endloop; end; --13:对每位员工的薪水进行判断,如果该员工薪水高于其所在部门的平均薪水,则将其薪水减50元,输出更新前后的薪水,员工姓名,所在部门编号。 --AVG([distinct|all]expr)over(analytic_clause) ---作用: --按照analytic_clause中的规则求分组平均值。 --分析函数语法: --FUNCTION_NAME(, ...) --OVER --( ) --PARTITION子句 --按照表达式分区(就是分组),如果省略了分区子句,则全部的结果集被看作是一个单一的组 select*fromemp1 DECLARE CURSOR crs_testAvg IS selectEMPNO,ENAME,JOB,SAL,DEPNO,AVG(SAL)OVER(PARTITIONBYDEPNO)ASDEP_AVG FROMEMP1forupdateofSAL; r_testAvgcrs_testAvg%rowtype; salInfoemp1.sal%type; begin forr_testAvgincrs_testAvgloop ifr_testAvg.SAL>r_testAvg.DEP_AVGthen salInfo:=r_testAvg.SAL-50; endif; updateemp1setSAL=salInfowherecurrentofcrs_testAvg; endloop; end;
总结
以上就是本文关于Oracle中游标Cursor基本用法详解的全部内容,希望对大家有所帮助,欢迎参阅:oracle数据库导入导出命令解析、ORACLESQL语句优化技术要点解析、浅谈oracle中单引号转义等,有什么问题可以随时留言,感谢大家!