SELECT [DISTICT|ALL]{*|column[,column,...]} INTO (variable[,variable,...] |record) FROM {table|(sub-query)}[alias] WHERE............ PL/SQL中SELECT语句只返回一行数据。如果超过一行数据,那么就要使用显式游标(对游标的讨论我们将在后面进行),INTO子句中要有与SELECT子句中相同列数量的变量。INTO子句中也可以是记录变量。
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 记录变量
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 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 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中的子查询没有什么区别。