ORACLE游标的用法

-- 声明游标;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;
END;

--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;
END;

--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;
END;

--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;
END;
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;
END;
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;
END;

--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;
END;
--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;
END;

--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;
END;

--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;
END;

--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;
END;

--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;
END;

--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;
 

 

你可能感兴趣的:(oracle,游标)