一、游标的定义
==游标==是++一种从多条数据的结果集中每次提取一行数据的机制。游标可以充当指针,可以便利结果集中所有的行,但是一次只能取一行值。++ 游标提供了在逐行的基础上操作表中数据的方法,游标的结果集是由select语句指定的。
1.静态游标和动态游标的区别?
==静态游标==的++结果集是固定的,中间不能改变。++ ==动态游标==的++结果集是不固定的,每次打开都可以更换结果集。++
2.显示游标和隐式游标的区别 ?
==显示游标==的结果集是++在declare部分声明的,中途不能改变。++ ==隐式游标==的++结果集不用声明,且游标无需打开、获取、关闭,这些系统会自动完成++。
3.游标的四个属性
==cur_name%isopen==:判断游标是否打开 (对,错) --布尔型; ==cur_name%found==:判断游标的指针是否有值(对,错)–布尔型; ==cur_name%notfound==:判断游标的指针是否没值(对,错)–布尔型; ==cur_name%rowcoun==:返回游标的指针指过的行数 --数值。
4.游标的循环
- ==普通循环==:%notfound;
- ==while循环==:%found;
- ==游标==:for循环。
/** 普通循环*/
DECLARE
CURSOR CUR_1 IS SELECT * FROM DEPT; --声明了一个静态的显示游标
V_DEPT DEPT%ROWTYPE;
begin
OPEN CUR_1;
LOOP
FETCH CUR_1 INTO V_DEPT;
EXIT WHEN CUR_1%NOTFOUND;--Y游标中没有数据直接退出
DBMS_OUTPUT.put_line(V_DEPT.DEPTNO||CHR(32)||V_DEPT.DNAME||CHR(32)||V_DEPT.LOC);
END LOOP;
CLOSE CUR_1;
END;
普通循环+%notfound的步骤: OPEN->LOOP->FETCH->EXIT WHEN %NOTFOUND->打印->END LOOP->CLOSE
/** while循环*/
DECLARE
CURSOR CUR_1 IS SELECT * FROM DEPT; --声明了一个静态的显示游标
V_DEPT DEPT%ROWTYPE;
BEGIN
OPEN CUR_1; --开
FETCH CUR_1 INTO V_DEPT;--先匹配一次在去判断
WHILE CUR_1%FOUND
LOOP
DBMS_OUTPUT.put_line(V_DEPT.DEPTNO||CHR(32)||V_DEPT.DNAME||CHR(32)||V_DEPT.LOC);
--先打印在匹配
FETCH CUR_1 INTO V_DEPT;
END LOOP;
CLOSE CUR_1;
END;
WHILE+%found的步骤:OPEN->FETCH->WHILE%FOUND->LOOP->先打印->FETCH->END LOOP->CLOSE;
5.静态游标
5.1显式游标–带参数
–打印某个部门某中职位的员工信息
DECLARE
CURSOR CUR_1(V_DEPTNO NUMBER,V_JOB VARCHAR2) IS
SELECT * FROM EMP WHERE DEPTNO=V_DEPTNO AND JOB=V_JOB;
V_E EMP%ROWTYPE;
BEGIN
OPEN CUR_1(10,'MANAGER'); --开游标时对参数进行赋值
LOOP
FETCH CUR_1 INTO V_E;
EXIT WHEN CUR_1%NOTFOUND;
DBMS_OUTPUT.put_line(V_E.ENAME||CHR(32)||V_E.JOB);
END LOOP;
CLOSE CUR_1;
DBMS_OUTPUT.put_line('---------------------------------');
OPEN CUR_1(20,'CLERK'); --重新打开游标时对参数进行赋值
LOOP
FETCH CUR_1 INTO V_E;
EXIT WHEN CUR_1%NOTFOUND;
DBMS_OUTPUT.put_line(V_E.ENAME||CHR(32)||V_E.JOB);
END LOOP;
CLOSE CUR_1;
end;
5.2静态游标–隐式游标
隐式游标不用申明 不用打开 不用赋值 不用关闭。 常见的隐私游标:DELETE /UPDATE/INSERT /SELECT INTO 单行赋值。
5.3 隐式游标的四个属性
sql%isopen:判断游标是否打开 永远返回错 sql%found:判断游标的指针是否有值(对,错)最近一次是否有结果–布尔型 sql%notfound:判断游标的指针是否没值(对,错) 最近一次是否没有结果–布尔型 sql%rowcount:返回游标的指针指过的行数 返回最近一次的记录数 --数值
DECLARE
V_E EMP%ROWTYPE;
BEGIN
UPDATE EMP_1 SET ENAME=LOWER(ENAME) WHERE DEPTNO=10;
IF SQL%FOUND THEN
DBMS_OUTPUT.put_line('真的更新了');
ELSIF SQL%NOTFOUND THEN
DBMS_OUTPUT.put_line('真的没更新了');
end IF;
DBMS_OUTPUT.put_line('更新了'||SQL%ROWCOUNT||'行');
DBMS_OUTPUT.put_line('=================================');
DELETE FROM EMP_1 WHERE JOB='MANAGER';
IF SQL%FOUND THEN
DBMS_OUTPUT.put_line('真的删了');
ELSIF SQL%NOTFOUND THEN
DBMS_OUTPUT.put_line('真的没删了');
end IF;
DBMS_OUTPUT.put_line('删了'||SQL%ROWCOUNT||'行');
DBMS_OUTPUT.put_line('=================================');
insert INTO EMP_1 SELECT * FROM EMP WHERE JOB='MANAGER';
IF SQL%FOUND THEN
DBMS_OUTPUT.put_line('真的插了');
ELSIF SQL%NOTFOUND THEN
DBMS_OUTPUT.put_line('真的没插了');
end IF;
DBMS_OUTPUT.put_line('插了'||SQL%ROWCOUNT||'行');
DBMS_OUTPUT.put_line('=================================');
SELECT EMPNO INTO V_E.EMPNO FROM EMP WHERE ENAME='KING';
IF SQL%FOUND THEN
DBMS_OUTPUT.put_line('真的FU了');
ELSIF SQL%NOTFOUND THEN
DBMS_OUTPUT.put_line('真的没FU了');
end IF;
DBMS_OUTPUT.put_line('FU了'||SQL%ROWCOUNT||'行');
DBMS_OUTPUT.put_line('=================================');
IF SQL%ISOPEN THEN
DBMS_OUTPUT.put_line('打开');
ELSE
DBMS_OUTPUT.put_line('真的没打');
END IF;
END;
6.动态游标
DECLARE
CUR_1 SYS_REFCURSOR; --直接声明了一个动态游标
V_D DATE;
V_C1 VARCHAR2(20);
V_C2 VARCHAR2(20);
BEGIN
--查询今年所有的星期五
OPEN CUR_1 FOR
SELECT * FROM (
SELECT DATE'2020-12-31'+LEVEL LV FROM DUAL CONNECT BY LEVEL<=365)
WHERE TO_CHAR(LV,'D')=6;
LOOP
FETCH CUR_1 INTO V_D;
EXIT WHEN CUR_1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(V_D);
END LOOP;
CLOSE CUR_1;
---------------------------
--查询emp中每个人的姓名和经理的姓名
OPEN CUR_1 FOR SELECT A.ENAME,B.ENAME FROM EMP A,EMP B
WHERE A.MGR=B.EMPNO(+);
LOOP
FETCH CUR_1 INTO V_C1,V_C2;
EXIT WHEN CUR_1%NOTFOUND;
DBMS_OUTPUT.put_line(V_C1||' '||V_C2);
END LOOP;
CLOSE CUR_1;
END;
--把入职日期看成是生日 查询每个人活了多少年又几个月
DECLARE
CUR_1 SYS_REFCURSOR;
V_E VARCHAR2(20);
V_N VARCHAR2(50);
BEGIN
OPEN CUR_1 FOR
SELECT ENAME,FLOOR(MONTHS_BETWEEN(SYSDATE,HIREDATE)/12)||'年'||
FLOOR(MOD(MONTHS_BETWEEN(SYSDATE,HIREDATE),12))||'月'
FROM EMP;
LOOP
FETCH CUR_1 INTO V_E,V_N;
EXIT WHEN CUR_1%NOTFOUND;
DBMS_OUTPUT.put_line(V_E||'活了'||V_N);
END LOOP;
CLOSE CUR_1;
END;
6.1动态游标 --强类型
可以更换结果集 但是不能更换结果集的格式。
REATE TABLE EMP_A AS SELECT * FROM EMP;
CREATE TABLE EMP_B AS SELECT * FROM EMP;
CREATE TABLE EMP_C AS SELECT * FROM EMP;
DECLARE
TYPE CUR_1_REF IS REF CURSOR RETURN EMP%ROWTYPE;--声明了一个强类型
cur_1 CUR_1_REF;--把声明的类型赋给变量 此时变量就成了强类型动态游标
v_E EMP%ROWTYPE;
BEGIN
DBMS_OUTPUT.put_line('EMP_A中的10部门的员工姓名和部门编号');
OPEN CUR_1 FOR SELECT * FROM EMP_A WHERE DEPTNO=10;
LOOP
FETCH CUR_1 INTO V_E;
EXIT WHEN CUR_1%NOTFOUND;
DBMS_OUTPUT.put_line(V_E.ENAME||' '||V_E.DEPTNO);
END LOOP;
CLOSE CUR_1;
DBMS_OUTPUT.put_line('EMP_B中工作是销售的员工姓名和工作');
OPEN CUR_1 FOR SELECT * FROM EMP_B WHERE JOB='SALESMAN';
LOOP
FETCH CUR_1 INTO V_E;
EXIT WHEN CUR_1%NOTFOUND;
DBMS_OUTPUT.put_line(V_E.ENAME||' '||V_E.JOB);
END LOOP;
CLOSE CUR_1;
DBMS_OUTPUT.put_line('EMP_C中1981年前入职的员工姓名和入职日期');
OPEN CUR_1 FOR SELECT * FROM EMP_C WHERE TO_CHAR(HIREDATE,'YYYY')<1981;
LOOP
FETCH CUR_1 INTO V_E;
EXIT WHEN CUR_1%NOTFOUND;
DBMS_OUTPUT.put_line(V_E.ENAME||' '||V_E.HIREDATE);
END LOOP;
CLOSE CUR_1;
END;
6.2动态游标 --弱类型
弱类型游标没有return,中途不仅可以更换结果集 还可以更换格式。
DECLARE
TYPE CUR_1_REF IS REF CURSOR; --声明了一个类型
CUR_1 CUR_1_REF;--声明了一个弱类型动态游标
V_N NUMBER;
V_C VARCHAR2(20);
BEGIN
DBMS_OUTPUT.put_line('每种职位的人数');
OPEN CUR_1 FOR SELECT JOB,COUNT(1) FROM EMP GROUP BY JOB;
LOOP
FETCH CUR_1 INTO V_C,V_N;
EXIT WHEN CUR_1%NOTFOUND;
DBMS_OUTPUT.put_line(V_C||' '||V_N);
END LOOP;
CLOSE CUR_1;
DBMS_OUTPUT.put_line('每个人的工资等级');
OPEN CUR_1 FOR SELECT ENAME,GRADE FROM EMP E,SALGRADE S
WHERE E.SAL BETWEEN S.LOSAL AND S.HISAL;
LOOP
FETCH CUR_1 INTO V_C,V_N;
EXIT WHEN CUR_1%NOTFOUND;
DBMS_OUTPUT.put_line(V_C||' '||V_N);
END LOOP;
CLOSE CUR_1;
END;
7.游标+for循环
7.1 带游标名
声明一个显示游标,不用打开、关闭和赋值。 因为有名字,所以可以使用游标属性
DECLARE
CURSOR CUR_1 IS SELECT * FROM EMP;
BEGIN
FOR I IN CUR_1
LOOP
DBMS_OUTPUT.put_line(I.EMPNO||' '||I.ENAME);
--EXIT WHEN CUR_1%ROWCOUNT=5; --因为有名字 可以使用游标属性
END LOOP;
END;
7.2、不带游标名
不用打开 不用赋值 不用声明 不用关闭 因为没有游标名字 所以不能使用游标属性
BEGIN
FOR I IN(SELECT * FROM EMP)
LOOP
DBMS_OUTPUT.put_line(I.EMPNO||' '||I.ENAME);
END LOOP;
END;