Cursor如何在 Oracle中使用

50次阅读
没有评论

共计 16760 个字符,预计需要花费 42 分钟才能阅读完成。

自动写代码机器人,免费开通

今天就跟大家聊聊有关 Cursor 如何在 Oracle 中使用,可能很多人都不太了解,为了让大家更加了解,丸趣 TV 小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

查询

SELECT 语句用于从数据库中查询数据,当在 PL/SQL 中使用 SELECT 语句时,要与 INTO 子句一起使用,查询的
返回值被赋予 INTO 子句中的变量,变量的声明是在 DELCARE 中。SELECT INTO 语法如下:

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_empno SCOTT.EMP.EMPNO%TYPE;
 v_salary EMP.SALARY%TYPE;

不但列名可以使用 %TYPE, 而且变量、游标、记录,或声明的常量都可以使用 %TYPE。这对于定义相同数据类
型的变量非常有用。

DELCARE
 V_A NUMBER(5):=10;
 V_B V_A%TYPE:=15;
 V_C V_A%TYPE;
 BEGIN
 DBMS_OUTPUT.PUT_LINE
 (V_A= ||V_A|| V_B= ||V_B|| V_C= ||V_C);
 SQL /
 V_A=10 V_B=15 V_C=
 PL/SQL procedure successfully completed.
 SQL

其他 DML 语句

其它操作数据的 DML 语句是:INSERT、UPDATE、DELETE 和 LOCK TABLE, 这些语句在 PL/SQL 中的语法与在 SQL 中
的语法相同。我们在前面已经讨

论过 DML 语句的使用这里就不再重复了。在 DML 语句中可以使用任何在 DECLARE 部分声明的变量,如果是嵌套
块,那么要注意变量的作用范围。

例:

CREATE OR REPLACE PROCEDURE FIRE_EMPLOYEE (pempno in number)
 v_ename EMP.ENAME%TYPE;
 BEGIN
 SELECT ename INTO v_ename
 FROM emp
 WHERE empno=p_empno;
 INSERT INTO FORMER_EMP(EMPNO,ENAME)
 VALUES (p_empno,v_ename);
 DELETE FROM emp
 WHERE empno=p_empno;
 UPDATE former_emp
 SET date_deleted=SYSDATE
 WHERE empno=p_empno;
 EXCEPTION
 WHEN NO_DATA_FOUND THEN
 DBMS_OUTPUT.PUT_LINE( Employee Number Not Found! 
 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 :SELECT INTO 至少返回一行

当 SQL%FOUND 为 TRUE 时,SQL%NOTFOUND 为 FALSE。

SQL%ROWCOUNT

在执行任何 DML 语句之前,SQL%ROWCOUNT 的值都是 NULL, 对于 SELECT INTO 语句,如果执行成功
,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 以及
SET TRANSACTION 语句。

COMMIT 语句终止事务,永久保存数据库的变化,同时释放所有 LOCK,ROLLBACK 终止现行事务释放所有 LOCK,
但不保存数据库的任何变化,SAVEPOI

NT 用于设置中间点,当事务调用过多的数据库操作时,中间点是非常有用的,SET TRANSACTION 用于设置事
务属性,比如 read-write 和隔离级等。

显式游标

查询返回结果超过一行时,就需要一个显式游标,此时用户不能使用 select into 语句。PL/SQL 管理隐式
游标,当查询开始时隐式游标打开,查询结束时隐式游标自动关闭。显式游标在 PL/SQL 块的声明部分声明,在执行部分或异常处理部分打开,取数据, 关闭。

使用游标

这里要做一个声明,我们所说的游标通常是指显式游标,因此从现在起没有特别指明的情况,我们所说的
游标都是指显式游标。要在程序中使用游标,必须首先声明游标。

声明游标

语法:

CURSOR cursor_name IS select_statement;

在 PL/SQL 中游标名是一个未声明变量,不能给游标名赋值或用于表达式中。

例:

DELCARE
 CURSOR C_EMP IS SELECT empno,ename,salary
 FROM emp
 WHERE salary 2000
 ORDER BY ename;
 ........
 BEGIN

在游标定义中 SELECT 语句中不一定非要表可以是视图,也可以从多个表或视图中选择的列,甚至可以使用 *
来选择所有的列。

打开游标

使用游标中的值之前应该首先打开游标,打开游标初始化查询处理。打开游标的语法是:

OPEN cursor_name

cursor_name 是在声明部分定义的游标名。

例:

OPEN C_EMP;

关闭游标

语法:

CLOSE cursor_name

例:

CLOSE C_EMP;

从游标提取数据

从游标得到一行数据使用 FETCH 命令。每一次提取数据后,游标都指向结果集的下一行。语法如下:

FETCH cursor_name INTO variable[,variable,…]

对于 SELECT 定义的游标的每一列,FETCH 变量列表都应该有一个变量与之相对应,变量的类型也要相同。

例:

SET SERVERIUTPUT ON
 DECLARE
 v_ename EMP.ENAME%TYPE;
 v_salary EMP.SALARY%TYPE;
 CURSOR c_emp IS SELECT ename,salary FROM emp;
 BEGIN
 OPEN c_emp;
 FETCH c_emp INTO v_ename,v_salary;
DBMS_OUTPUT.PUT_LINE(Salary of Employee || v_ename || is || v_salary);
 FETCH c_emp INTO v_ename,v_salary;
 DBMS_OUTPUT.PUT_LINE(Salary of Employee || v_ename || is || v_salary);
 FETCH c_emp INTO v_ename,v_salary;
 DBMS_OUTPUT.PUT_LINE(Salary of Employee || v_ename || is || v_salary);
 CLOSE c_emp;
 END

这段代码无疑是非常麻烦的,如果有多行返回结果,可以使用循环并用游标属性为结束循环的条件,以这
种方式提取数据,程序的可读性和简洁性都大为提高,下面我们使用循环重新写上面的程序:

SET SERVERIUTPUT ON
 DECLARE
 v_ename EMP.ENAME%TYPE;
 v_salary EMP.SALARY%TYPE;
 CURSOR c_emp IS SELECT ename,salary FROM emp;
 BEGIN
 OPEN c_emp;
 LOOP
 FETCH c_emp INTO v_ename,v_salary;
 EXIT WHEN c_emp%NOTFOUND;
 DBMS_OUTPUT.PUT_LINE(Salary of Employee || v_ename || is || v_salary);
 END

记录变量

定义一个记录变量使用 TYPE 命令和 %ROWTYPE,关于 %ROWsTYPE 的更多信息请参阅相关资料。

记录变量用于从游标中提取数据行,当游标选择很多列的时候,那么使用记录比为每列声明一个变量要方
便得多。

当在表上使用 %ROWTYPE 并将从游标中取出的值放入记录中时,如果要选择表中所有列,那么在 SELECT 子句
中使用 * 比将所有列名列出来要安全得多。

例:

SET SERVERIUTPUT ON
 DECLARE
 R_emp EMP%ROWTYPE;
 CURSOR c_emp IS SELECT * FROM emp;
 BEGIN
 OPEN c_emp;
 LOOP
 FETCH c_emp INTO r_emp;
 EXIT WHEN c_emp%NOTFOUND;
 DBMS_OUT.PUT.PUT_LINE(Salary of Employee ||r_emp.ename|| is || r_emp.salary);
 END LOOP;
 CLOSE c_emp;
 END;

%ROWTYPE 也可以用游标名来定义,这样的话就必须要首先声明游标:

SET SERVERIUTPUT ON
 DECLARE
 CURSOR c_emp IS SELECT ename,salary FROM emp;
 R_emp c_emp%ROWTYPE;
 BEGIN
 OPEN c_emp;
 LOOP
 FETCH c_emp INTO r_emp;
 EXIT WHEN c_emp%NOTFOUND;
 DBMS_OUT.PUT.PUT_LINE(Salary of Employee ||r_emp.ename|| is || r_emp.salary);
 END LOOP;
 CLOSE c_emp;
 END;

带参数的游标

与存储过程和函数相似,可以将参数传递给游标并在查询中使用。这对于处理在某种条件下打开游标的情
况非常有用。它的语法如下:

CURSOR cursor_name[(parameter[,parameter],…)] IS select_statement;

定义参数的语法如下:

Parameter_name [IN] data_type[{:=|DEFAULT} value]

与存储过程不同的是,游标只能接受传递的值,而不能返回值。参数只定义数据类型,没有大小。

另外可以给参数设定一个缺省值,当没有参数值传递给游标时,就使用缺省值。游标中定义的参数只是一个占位符,在别处引用该参数不一定可靠。

在打开游标时给参数赋值,语法如下:

OPEN cursor_name[value[,value]….];

参数值可以是文字或变量。

例:

DECALRE
 CURSOR c_dept IS SELECT * FROM dept ORDER BY deptno;
 CURSOR c_emp (p_dept VARACHAR2) IS
 SELECT ename,salary
 FROM emp
 WHERE deptno=p_dept
 ORDER BY ename
 r_dept DEPT%ROWTYPE;
 v_ename EMP.ENAME%TYPE;
 v_salary EMP.SALARY%TYPE;
 v_tot_salary EMP.SALARY%TYPE;
 BEGIN
 OPEN c_dept;
 LOOP
 FETCH c_dept INTO r_dept;
 EXIT WHEN c_dept%NOTFOUND;
 DBMS_OUTPUT.PUT_LINE(Department: || r_dept.deptno|| - ||r_dept.dname);
 v_tot_salary:=0;
 OPEN c_emp(r_dept.deptno);
 LOOP
 FETCH c_emp INTO v_ename,v_salary;
 EXIT WHEN c_emp%NOTFOUND;
 DBMS_OUTPUT.PUT_LINE(Name: || v_ename||  salary: ||v_salary);
 v_tot_salary:=v_tot_salary+v_salary;
 END LOOP;
 CLOSE c_emp;
 DBMS_OUTPUT.PUT_LINE(Toltal Salary for dept: || v_tot_salary);
 END LOOP;
 CLOSE c_dept;
 END;

游标 FOR 循环

在大多数时候我们在设计程序的时候都遵循下面的步骤:

1、打开游标
2、开始循环
3、从游标中取值
4、检查那一行被返回
5、处理
6、关闭循环
7、关闭游标

可以简单的把这一类代码称为游标用于循环。但还有一种循环与这种类型不相同,这就是 FOR 循环,用于
FOR 循环的游标按照正常的声明方式声明,它的优点在于不需要显式的打开、关闭、取数据,测试数据的存在、定义存放数据的变量等等。

游标 FOR 循环的语法如下:

FOR record_name IN
 (corsor_name[(parameter[,parameter]...)]
 | (query_difinition)
 LOOP
 statements
 END LOOP;

下面我们用 for 循环重写上面的例子:

DECALRE
 CURSOR c_dept IS SELECT deptno,dname FROM dept ORDER BY deptno;
 CURSOR c_emp (p_dept VARACHAR2) IS
 SELECT ename,salary
 FROM emp
 WHERE deptno=p_dept
 ORDER BY ename
 v_tot_salary EMP.SALARY%TYPE;
BEGIN
 FOR r_dept IN c_dept LOOP
 DBMS_OUTPUT.PUT_LINE(Department: || r_dept.deptno|| - ||r_dept.dname);
 v_tot_salary:=0;
 FOR r_emp IN c_emp(r_dept.deptno) LOOP
 DBMS_OUTPUT.PUT_LINE(Name:  || v_ename ||  salary:  || v_salary);
 v_tot_salary:=v_tot_salary+v_salary;
 END LOOP;
 DBMS_OUTPUT.PUT_LINE(Toltal Salary for dept: || v_tot_salary);
 END LOOP;
 END;

在游标 FOR 循环中使用查询

在游标 FOR 循环中可以定义查询,由于没有显式声明所以游标没有名字,记录名通过游标查询来定义。

DECALRE
 v_tot_salary EMP.SALARY%TYPE;
 BEGIN
 FOR r_dept IN (SELECT deptno,dname FROM dept ORDER BY deptno) LOOP
 DBMS_OUTPUT.PUT_LINE(Department: || r_dept.deptno|| - ||r_dept.dname);
 v_tot_salary:=0;
 FOR r_emp IN (SELECT ename,salary
 FROM emp
 WHERE deptno=p_dept
 ORDER BY ename) LOOP
 DBMS_OUTPUT.PUT_LINE(Name: || v_ename||  salary: ||v_salary);
 v_tot_salary:=v_tot_salary+v_salary;
 END LOOP;
 DBMS_OUTPUT.PUT_LINE(Toltal Salary for dept: || v_tot_salary);
 END LOOP;
 END;

游标中的子查询

语法如下:

CURSOR C1 IS SELECT * FROM emp
 WHERE deptno NOT IN (SELECT deptno
 FROM dept
 WHERE dname!= ACCOUNTING

可以看出与 SQL 中的子查询没有什么区别。

游标中的更新和删除

在 PL/SQL 中依然可以使用 UPDATE 和 DELETE 语句更新或删除数据行。显式游标只有在需要获得多行数据的情
况下使用。PL/SQL 提供了仅仅使 用游标就可以执行删除或更新记录的方法。
UPDATE 或 DELETE 语句中的 WHERE CURRENT OF 子串专门处理要执行 UPDATE 或 DELETE 操作的表中取出的最近的
数据。要使用这个方法,在声明游标 时必须使用 FOR UPDATE 子串,当对话使用 FOR UPDATE 子串打开一个游标时,所有返回集中的数据行都将处于行级(ROW-LEVEL)独占式锁定,其
他对象只能查询这些数据行,不能进行 UPDATE、DELETE 或 SELECT…FOR  UPDATE 操作。

语法:

FOR UPDATE [OF [schema.]table.column[,[schema.]table.column]..
[nowait]

在多表查询中,使用 OF 子句来锁定特定的表, 如果忽略了 OF 子句,那么所有表中选择的数据行都将被锁定。
如果这些数据行已经被其他会话锁定,那么正常情况下 ORACLE 将等待,直到数据行解锁。

在 UPDATE 和 DELETE 中使用 WHERE CURRENT OF 子串的语法如下:

WHERE{CURRENT OF cursor_name|search_condition}

例:

DELCARE
 CURSOR c1 IS SELECT empno,salary
 FROM emp
 WHERE comm IS NULL
 FOR UPDATE OF comm;
 v_comm NUMBER(10,2);
 BEGIN
 FOR r1 IN c1 LOOP
 IF r1.salary 500 THEN
 v_comm:=r1.salary*0.25;
 ELSEIF r1.salary 1000 THEN
 v_comm:=r1.salary*0.20;
 ELSEIF r1.salary 3000 THEN
 v_comm:=r1.salary*0.15;
 ELSE
 v_comm:=r1.salary*0.12;
 END IF;
 UPDATE emp;
 SET comm=v_comm
 WHERE CURRENT OF c1l;
 END LOOP;
 END
 - 声明游标
-- 宗地表的调查日期 LANDINFO_RESEARCHDATE
-- 复制到流程表的权属调查时间 FLOW_REASEARCHTIME
DECLARE 
cursor cur_sel_all is select LANDINFO_RESEARCHDATE,LANDINFO_LANDNO from t_leoa_landinfo; -- 定义游标
 l_date t_leoa_landinfo.landinfo_researchdate%type; -- 声明变量分别保存 t_leoa_landinfo 的各列
 l_landNo t_leoa_landinfo.landinfo_landno%type; 
begin
open cur_sel_all;
 loop -- 循环取数,并将游标数据填充到返回纪录集合中
 fetch cur_sel_all into l_date,l_landNo;
 exit when cur_sel_all%NOTFOUND; -- 循环退出条件
 if cur_sel_all%FOUND then -- 获取数据  
 update T_LEOA_BOOKFLOW t2 set FLOW_REASEARCHTIME = l_date where l_landNo = t2.landinfo_landno; 
 end if;
 end loop;
close cur_sel_all;
end;

下面再分享一下另外一则游标使用方法的代码,具体如下:

--  声明游标;CURSOR cursor_name IS select_statement
--For  循环游标
--(1)定义游标
--(2)定义游标变量
--(3)使用 for 循环来使用这个游标
declare
 -- 类型定义
 cursor c_job
 is
 select empno,ename,job,sal
 from emp
 where job= MANAGER 
 -- 定义一个游标变量 v_cinfo c_emp%ROWTYPE ,该类型为游标 c_emp 中的一行数据类型
 c_row c_job%rowtype;
begin
 for c_row in c_job loop
 dbms_output.put_line(c_row.empno|| - ||c_row.ename|| - ||c_row.job|| - ||c_row.sal);
 end loop;
--Fetch 游标
-- 使用的时候必须要明确的打开和关闭
declare 
 -- 类型定义
 cursor c_job
 is
 select empno,ename,job,sal
 from emp
 where job= MANAGER 
 -- 定义一个游标变量
 c_row c_job%rowtype;
begin
 open c_job;
 loop
 -- 提取一行数据到 c_row
 fetch c_job into c_row;
 -- 判读是否提取到值,没取到值就退出
 -- 取到值 c_job%notfound  是 false 
 -- 取不到值 c_job%notfound  是 true
 exit when c_job%notfound;
 dbms_output.put_line(c_row.empno|| - ||c_row.ename|| - ||c_row.job|| - ||c_row.sal);
 end loop;
 -- 关闭游标
 close c_job;
--1:任意执行一个 update 操作,用隐式游标 sql 的属性 %found,%notfound,%rowcount,%isopen 观察 update 语句的执行情况。 begin
 update emp set ENAME= ALEARK  WHERE EMPNO=7469;
 if sql%isopen then
 dbms_output.put_line( Openging 
 else
 dbms_output.put_line( closing 
 end if;
 if sql%found then
 dbms_output.put_line( 游标指向了有效行 -- 判断游标是否指向有效行
 else
 dbms_output.put_line( Sorry 
 end if;
 if sql%notfound then
 dbms_output.put_line( Also Sorry 
 else
 dbms_output.put_line( Haha 
 end if;
 dbms_output.put_line(sql%rowcount);
 exception 
 when no_data_found then
 dbms_output.put_line( Sorry No data 
 when too_many_rows then
 dbms_output.put_line( Too Many rows 
 end;
declare
 empNumber emp.EMPNO%TYPE;
 empName emp.ENAME%TYPE;
 begin
 if sql%isopen then
 dbms_output.put_line( Cursor is opinging 
 else
 dbms_output.put_line( Cursor is Close 
 end if;
 if sql%notfound then
 dbms_output.put_line( No Value 
 else
 dbms_output.put_line(empNumber);
 end if;
 dbms_output.put_line(sql%rowcount);
 dbms_output.put_line( ------------- 
 select EMPNO,ENAME into empNumber,empName from emp where EMPNO=7499;
 dbms_output.put_line(sql%rowcount);
 if sql%isopen then
 dbms_output.put_line( Cursor is opinging 
 else
 dbms_output.put_line( Cursor is Closing 
 end if;
 if sql%notfound then
 dbms_output.put_line( No Value 
 else
 dbms_output.put_line(empNumber);
 end if;
 exception 
 when no_data_found then
 dbms_output.put_line( No Value 
 when too_many_rows then
 dbms_output.put_line( too many rows 
 end;
--2, 使用游标和 loop 循环来显示所有部门的名称
-- 游标声明
declare 
 cursor csr_dept
 is
 --select 语句
 select DNAME
 from Depth;
 -- 指定行指针, 这句话应该是指定和 csr_dept 行类型相同的变量
 row_dept csr_dept%rowtype;
begin
 --for 循环
 for row_dept in csr_dept loop
 dbms_output.put_line(部门名称: ||row_dept.DNAME);
 end loop;
--3, 使用游标和 while 循环来显示所有部门的的地理位置(用 %found 属性)declare
 -- 游标声明
 cursor csr_TestWhile
 is
 --select 语句
 select LOC
 from Depth;
 -- 指定行指针
 row_loc csr_TestWhile%rowtype;
begin
 -- 打开游标
 open csr_TestWhile;
 -- 给第一行喂数据
 fetch csr_TestWhile into row_loc;
 -- 测试是否有数据,并执行循环
 while csr_TestWhile%found loop
 dbms_output.put_line(部门地点:||row_loc.LOC);
 -- 给下一行喂数据
 fetch csr_TestWhile into row_loc;
 end loop;
 close csr_TestWhile;
end; 
select * from emp
 
--4, 接收用户输入的部门编号,用 for 循环和游标,打印出此部门的所有雇员的所有信息(使用循环游标)
--CURSOR cursor_name[(parameter[,parameter],...)] IS select_statement;
-- 定义参数的语法如下:Parameter_name [IN] data_type[{:=|DEFAULT} value] 
declare 
 CURSOR 
 c_dept(p_deptNo number)
 is
 select * from emp where emp.depno=p_deptNo;
 r_emp emp%rowtype;
begin
 for r_emp in c_dept(20) loop
 dbms_output.put_line(员工号:||r_emp.EMPNO|| 员工名:||r_emp.ENAME|| 工资:||r_emp.SAL);
 end loop;
select * from emp 
--5:向游标传递一个工种,显示此工种的所有雇员的所有信息(使用参数游标)
declare 
 cursor
 c_job(p_job nvarchar2)
 is 
 select * from emp where JOB=p_job;
 r_job emp%rowtype;
begin 
 for r_job in c_job(CLERK) loop
 dbms_output.put_line(员工号 ||r_job.EMPNO||   || 员工姓名 ||r_job.ENAME);
 end loop;
SELECT * FROM EMP
--6:用更新游标来为雇员加佣金:(用 if 实现, 创建一个与 emp 表一摸一样的 emp1 表,对 emp1 表进行修改操作), 并将更新前后的数据输出出来  
--http://zheng12tian.iteye.com/blog/815770 
 create table emp1 as select * from emp;
declare
 cursor
 csr_Update
 is
 select * from emp1 for update OF SAL;
 empInfo csr_Update%rowtype;
 saleInfo emp1.SAL%TYPE;
begin
 FOR empInfo IN csr_Update LOOP
 IF empInfo.SAL 1500 THEN
 saleInfo:=empInfo.SAL*1.2;
 elsif empInfo.SAL 2000 THEN
 saleInfo:=empInfo.SAL*1.5;
 elsif empInfo.SAL 3000 THEN
 saleInfo:=empInfo.SAL*2;
 END IF;
 UPDATE emp1 SET SAL=saleInfo WHERE CURRENT OF csr_Update;
 END LOOP;
--7: 编写一个 PL/SQL 程序块,对名字以‘A 或‘S 开始的所有雇员按他们的基本薪水 (sal) 的 10% 给他们加薪(对 emp1 表进行修改操作)
declare 
 cursor
 csr_AddSal
 is
 select * from emp1 where ENAME LIKE  A%  OR ENAME LIKE  S%  for update OF SAL;
 r_AddSal csr_AddSal%rowtype;
 saleInfo emp1.SAL%TYPE;
begin
 for r_AddSal in csr_AddSal loop
 dbms_output.put_line(r_AddSal.ENAME|| 原来的工资: ||r_AddSal.SAL);
 saleInfo:=r_AddSal.SAL*1.1;
 UPDATE emp1 SET SAL=saleInfo WHERE CURRENT OF csr_AddSal;
 end loop;
--8:编写一个 PL/SQL 程序块,对所有的 salesman 增加佣金(comm)500
declare
 cursor
 csr_AddComm(p_job nvarchar2)
 is
 select * from emp1 where JOB=p_job FOR UPDATE OF COMM;
 r_AddComm emp1%rowtype;
 commInfo emp1.comm%type;
begin
 for r_AddComm in csr_AddComm(SALESMAN) LOOP
 commInfo:=r_AddComm.COMM+500;
 UPDATE EMP1 SET COMM=commInfo where CURRENT OF csr_AddComm;
 END LOOP;
--9:编写一个 PL/SQL 程序块,以提升 2 个资格最老的职员为 MANAGER(工作时间越长,资格越老)--(提示:可以定义一个变量作为计数器控制游标只提取两条数据;也可以在声明游标的时候把雇员中资格最老的两个人查出来放到游标中。)declare
 cursor crs_testComput
 is
 select * from emp1 order by HIREDATE asc;
 -- 计数器
 top_two number:=2;
 r_testComput crs_testComput%rowtype;
begin
 open crs_testComput;
 FETCH crs_testComput INTO r_testComput;
 while top_two 0 loop
 dbms_output.put_line(员工姓名:||r_testComput.ENAME||   工作时间:||r_testComput.HIREDATE);
 -- 计速器减一
 top_two:=top_two-1;
 FETCH crs_testComput INTO r_testComput;
 end loop;
 close crs_testComput;
--10:编写一个 PL/SQL 程序块,对所有雇员按他们的基本薪水 (sal) 的 20% 为他们加薪,-- 如果增加的薪水大于 300 就取消加薪(对 emp1 表进行修改操作,并将更新前后的数据输出出来) 
declare
 cursor
 crs_UpadateSal
 is
 select * from emp1 for update of SAL;
 r_UpdateSal crs_UpadateSal%rowtype;
 salAdd emp1.sal%type;
 salInfo emp1.sal%type;
begin
 for r_UpdateSal in crs_UpadateSal loop
 salAdd:= r_UpdateSal.SAL*0.2;
 if salAdd 300 then
 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);
 end if;
 update emp1 set SAL=salInfo where current of crs_UpadateSal;
 end loop;
--11: 将每位员工工作了多少年零多少月零多少天输出出来  
 --CEIL(n)函数:取大于等于数值 n 的最小整数
 --FLOOR(n)函数:取小于等于数值 n 的最大整数
 --truc 的用法  http://publish.it168.com/2005/1028/20051028034101.shtml
declare
 cursor
 crs_WorkDay
 is
 select ENAME,HIREDATE, trunc(months_between(sysdate, hiredate) / 12) AS SPANDYEARS,
 trunc(mod(months_between(sysdate, hiredate), 12)) AS months,
 trunc(mod(mod(sysdate - hiredate, 365), 12)) as days
 from emp1;
 r_WorkDay crs_WorkDay%rowtype;
begin
 for r_WorkDay in crs_WorkDay loop
 dbms_output.put_line(r_WorkDay.ENAME|| 已经工作了 ||r_WorkDay.SPANDYEARS|| 年, 零 ||r_WorkDay.months|| 月, 零 ||r_WorkDay.days|| 天 
 end loop;
--12: 输入部门编号,按照下列加薪比例执行(用 CASE 实现,创建一个 emp1 表,修改 emp1 表的数据), 并将更新前后的数据输出出来
-- deptno raise(%)
-- 10 5%
-- 20 10%
-- 30 15%
-- 40 20%
--  加薪比例以现有的 sal 为标准
--CASE expr WHEN comparison_expr THEN return_expr
--[, WHEN comparison_expr THEN return_expr]... [ELSE else_expr] END
declare
 cursor
 crs_caseTest
 is
 select * from emp1 for update of SAL;
 r_caseTest crs_caseTest%rowtype;
 salInfo emp1.sal%type;
 begin
 for r_caseTest in crs_caseTest loop
 case 
 when r_caseTest.DEPNO=10
 THEN salInfo:=r_caseTest.SAL*1.05;
 when r_caseTest.DEPNO=20
 THEN salInfo:=r_caseTest.SAL*1.1;
 when r_caseTest.DEPNO=30
 THEN salInfo:=r_caseTest.SAL*1.15;
 when r_caseTest.DEPNO=40
 THEN salInfo:=r_caseTest.SAL*1.2;
 end case;
 update emp1 set SAL=salInfo where current of crs_caseTest;
 end loop;
--13: 对每位员工的薪水进行判断,如果该员工薪水高于其所在部门的平均薪水,则将其薪水减 50 元,输出更新前后的薪水,员工姓名,所在部门编号。--AVG([distinct|all] expr) over (analytic_clause)
--- 作用:-- 按照 analytic_clause 中的规则求分组平均值。 -- 分析函数语法:
 --FUNCTION_NAME(argument , argument ...)
 --OVER
 --(Partition-Clause Order-by-Clause Windowing Clause)
 --PARTITION 子句
 -- 按照表达式分区(就是分组), 如果省略了分区子句, 则全部的结果集被看作是一个单一的组
 select * from emp1
DECLARE
 CURSOR 
 crs_testAvg
 IS
 select EMPNO,ENAME,JOB,SAL,DEPNO,AVG(SAL) OVER (PARTITION BY DEPNO ) AS DEP_AVG
 FROM EMP1 for update of SAL;
 r_testAvg crs_testAvg%rowtype;
 salInfo emp1.sal%type;
 begin
 for r_testAvg in crs_testAvg loop
 if r_testAvg.SAL r_testAvg.DEP_AVG then
 salInfo:=r_testAvg.SAL-50;
 end if;
 update emp1 set SAL=salInfo where current of crs_testAvg;
 end loop;
end;

看完上述内容,你们对 Cursor 如何在 Oracle 中使用有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注丸趣 TV 行业资讯频道,感谢大家的支持。

向 AI 问一下细节

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-12-04发表,共计16760字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)