作者:gqk
游標 CURSOR:
一、游標概述:
游標(cursor)是數據庫系統在內存中開設的一個數據緩沖區,存放SQL語句的執行結果。
每個游標都有一個名字,用戶可以用SQL語句逐一從游標中獲取記錄,並賦給變量做進一步處理。
作用:用於定位結果集的行 和 遍歷結果集。
二、游標分類:
- 顯式游標:在前述程序中用到的SELECT...INTO...查詢語句,一次只能從數據庫中提取一行數據,對於這種形式的查詢和DML操作,系統都會使用一個隱式游標
- 隱式游標:但是如果要提取多行數據,就要由程序員定義一個顯式游標,並通過與游標有關的語句進行處理。顯式游標對應一個返回結果為多行多列的SELECT語句。

三:顯示游標:
處理顯示游標需要的四個步驟:
- 定義游標:就是定義一個游標的名稱,以及與其對應的SELECT語句,形式如下
-
- CURSOR 游標名稱 IS select_statement
-
- 打開游標 :open 游標變量(不能重復打開游標)
-
- 打開游標時,SELECT語句的查詢結果就被傳送到了游標工作區。
-
- 提取數據 :fetch 游標變量 into 變量1,變量2,……
-
- 提取操作必須在打開游標之后進行。
-
- 關閉游標:close 游標變量
顯式游標打開后,必須顯式地關閉。游標一旦關閉,游標占用的資源就被釋放,游標變成無效,必須重新打開才能使用。
--查詢輸出所有員工的編號,姓名,工資:(當執行fetch時提取數據指針下移一行)
DECLARE
v_empid employees.employee_id%TYPE;
v_name employees.last_name%TYPE;
v_salary employees.salary%TYPE;
CURSOR emp_cursor IS
SELECT employee_id,last_name,salary
FROM employees
WHERE department_id=90;
BEGIN
OPEN emp_cursor;
FETCH emp_cursor INTO v_empid,v_name,v_salary;--默認指向第一行
FETCH emp_cursor INTO v_empid,v_name,v_salary;
FETCH emp_cursor INTO v_empid,v_name,v_salary;
dbms_output.put_line('編號:' || v_empid);
dbms_output.put_line('姓名:' || v_name);
dbms_output.put_line('工資:' || v_salary);
CLOSE emp_cursor;
END;
四:游標的四個屬性
游標變量%found:當最近一次讀記錄時成功返回,則值為TRUE
游標變量%notfound:同上,求反
游標變量%isopen:判斷游標是否已經打開
游標變量%rowcount:返回已從游標中讀取的記錄數
--查詢輸出所有員工的編號,姓名,工資
loop循環
DECLARE
v_empid employees.employee_id%TYPE;
v_name employees.last_name%TYPE;
v_salary employees.salary%TYPE;
CURSOR emp_cursor IS
SELECT employee_id,last_name,salary
FROM employees;
BEGIN
OPEN emp_cursor;
LOOP
FETCH emp_cursor INTO v_empid,v_name,v_salary;
EXIT WHEN emp_cursor%NOTFOUND;--讀取最后一次失敗后跳出循環
dbms_output.put_line('編號:' || v_empid);
dbms_output.put_line('姓名:' || v_name);
dbms_output.put_line('工資:' || v_salary);
dbms_output.put_line('--------------------');
END LOOP;
CLOSE emp_cursor;
END;
while 循環
DECLARE
v_empid employees.employee_id%TYPE;
v_name employees.last_name%TYPE;
v_salary employees.salary%TYPE;
CURSOR emp_cursor IS
SELECT employee_id,last_name,salary
FROM employees;
BEGIN
OPEN emp_cursor;
FETCH emp_cursor INTO v_empid,v_name,v_salary;
WHILE emp_cursor%FOUND LOOP
dbms_output.put_line('編號:' || v_empid);
dbms_output.put_line('姓名:' || v_name);
dbms_output.put_line('工資:' || v_salary);
dbms_output.put_line('--------------------');
FETCH emp_cursor INTO v_empid,v_name,v_salary;
END LOOP;
CLOSE emp_cursor;
END;
--查詢輸出所有員工的編號,姓名,工資(PLSQL表類型)
DECLARE
TYPE emp_table_type IS TABLE OF employees%ROWTYPE
INDEX BY BINARY_INTEGER;
e emp_table_type;
CURSOR emp_cursor IS
SELECT *
FROM employees;
BEGIN
OPEN emp_cursor;
FETCH emp_cursor BULK COLLECT INTO e;
FOR i IN 1..e.COUNT LOOP
dbms_output.put_line('編號:' || e(i).employee_id);
dbms_output.put_line('姓名:' || e(i).last_name);
dbms_output.put_line('工資:' || e(i).salary);
dbms_output.put_line('--------------------');
END LOOP;
CLOSE emp_cursor;
END;
--查詢輸出所有員工的編號,姓名,工資(通過limit控制提取的數據量)每次提取6條數據
DECLARE
TYPE emp_table_type IS TABLE OF employees%ROWTYPE
INDEX BY BINARY_INTEGER;
e emp_table_type;
v_count NUMBER := 1;
CURSOR emp_cursor IS
SELECT *
FROM employees;
BEGIN
OPEN emp_cursor;
LOOP
dbms_output.put_line('第' || v_count || '次FETCH');
FETCH emp_cursor BULK COLLECT INTO e LIMIT 6;
FOR i IN 1..e.COUNT LOOP
dbms_output.put_line('編號:' || e(i).employee_id);
dbms_output.put_line('姓名:' || e(i).last_name);
dbms_output.put_line('工資:' || e(i).salary);
dbms_output.put_line('--------------------');
END LOOP;
EXIT WHEN emp_cursor%NOTFOUND;
v_count := v_count + 1;
END LOOP;
CLOSE emp_cursor;
END;
--查詢輸出某個部門的員工的編號,姓名,工資(參數化游標)
DECLARE
v_empid employees.employee_id%TYPE;
v_name employees.last_name%TYPE;
v_salary employees.salary%TYPE;
CURSOR emp_cursor(p_deptid employees.department_id%TYPE) IS
SELECT employee_id,last_name,salary
FROM employees
WHERE department_id=p_deptid;
BEGIN
OPEN emp_cursor(50);
LOOP
FETCH emp_cursor INTO v_empid,v_name,v_salary;
EXIT WHEN emp_cursor%NOTFOUND;
dbms_output.put_line('編號:' || v_empid || ',姓名:' || v_name || ',工資:' || v_salary);
END LOOP;
CLOSE emp_cursor;
dbms_output.put_line('----------------------');
OPEN emp_cursor(90);
LOOP
FETCH emp_cursor INTO v_empid,v_name,v_salary;
EXIT WHEN emp_cursor%NOTFOUND;
dbms_output.put_line('編號:' || v_empid || ',姓名:' || v_name || ',工資:' || v_salary);
END LOOP;
CLOSE emp_cursor;
END;
五:游標for循環:
PL/SQL中提供了游標for循環語句,可以自動的執行游標的open,fetch,close語句和循環語句的功能,當進入循環時,游標for循環語句自動打開游標,並提取第一行數據,提取
提取完成后自動進入下一個提取,提取完成后自動關閉游標:
--查詢輸出所有員工的編號,姓名,工資
DECLARE
CURSOR emp_cursor IS
SELECT *
FROM employees;
BEGIN
FOR e IN emp_cursor LOOP
dbms_output.put_line(e.employee_id||','||e.last_name||','||e.salary);
END LOOP;
END;
--查詢輸出所有員工的編號,姓名,工資(帶參數)
DECLARE
CURSOR emp_cursor(p_deptid employees.department_id%TYPE) IS
SELECT *
FROM employees
WHERE department_id=p_deptid;
BEGIN
FOR e IN emp_cursor(90) LOOP
dbms_output.put_line(e.employee_id||','||e.last_name||','||e.salary);
END LOOP;
END;
--查詢輸出所有員工的編號,姓名,工資(最精簡寫法)
DECLARE
BEGIN
FOR e IN (SELECT * FROM employees) LOOP
dbms_output.put_line(e.employee_id||','||e.last_name||','||e.salary);
END LOOP;
END;
--輸出每個部門的部門編號,部門名稱以及這個部門的下屬員工的編號,姓名,工資
/*
10 nec
100,tom,2400
101,jack,17000
20 ge
102,rose,2600
xxx,xxx,xxx
xxx,xxx,xxx
40 hsw
xxx,xxx,xxx
*/
DECLARE
CURSOR dept_cursor IS
SELECT * FROM departments;
CURSOR emp_cursor(p_deptid NUMBER) IS
SELECT * FROM employees
WHERE department_id=p_deptid;
BEGIN
FOR d IN dept_cursor LOOP
dbms_output.put_line(d.department_id || ' ' || d.department_name);
FOR e IN emp_cursor(d.department_id) LOOP
dbms_output.put_line(' ' || e.employee_id || ',' || e.last_name || ',' || e.salary);
END LOOP;
END LOOP;
END;
隱式游標:固定名稱SQL
SQL%FOUND:如果操作有影響的行,就為TRUE,否則為FALSE
SQL%NOTFOUND:求反
SQL%ISOPEN:在隱式游標中,取值永遠為FALSE
SQL%ROWCOUNT:操作影響的行數
*/
--如果有員工被更新,輸出“更新成功,有XX個員工被更新”
--如果沒有任何員工被更新,輸出“這個部門不存在,沒有任何員工被更新”
DECLARE
v_deptid NUMBER := &input;
BEGIN
UPDATE employees
SET salary = salary + 1
WHERE department_id = v_deptid;
IF SQL%FOUND THEN
dbms_output.put_line('更新成功,有' || SQL%ROWCOUNT || '個員工被更新');
ELSE
dbms_output.put_line('這個部門不存在,沒有任何員工被更新');
END IF;
COMMIT;
END;
--通過游標去更新員工的工資,如果工資低於5000塊,則把工資改為5000
DECLARE
CURSOR emp_cursor IS
SELECT * FROM new_emp FOR UPDATE;
BEGIN
FOR e IN emp_cursor LOOP
IF e.salary<5000 THEN
UPDATE new_emp SET salary=5000 WHERE CURRENT OF emp_cursor;
END IF;
END LOOP;
END;
--通過游標刪除工資=5000的員工
DECLARE
CURSOR emp_cursor IS
SELECT * FROM new_emp FOR UPDATE;
BEGIN
FOR e IN emp_cursor LOOP
IF e.salary=5000 THEN
DELETE FROM new_emp WHERE CURRENT OF emp_cursor;
END IF;
END LOOP;
END;
--游標變量(動態游標)
--用戶輸入一個字母,輸入E,輸出所有員工姓名,如果輸入D,輸出所有部門的名稱
DECLARE
v_cmd CHAR(1) := '&input';
v_name VARCHAR2(50);
--聲明自定義的游標變量類型
--TYPE c_type IS REF CURSOR;
--聲明游標變量
--c c_type;
c SYS_REFCURSOR;--代替TYPE c_type IS REF CURSOR
BEGIN
IF v_cmd='E' THEN
dbms_output.put_line('員工姓名');
OPEN c FOR SELECT last_name FROM employees;
ELSIF v_cmd='D' THEN
dbms_output.put_line('部門名稱');
OPEN c FOR SELECT department_name FROM departments;
ELSE
dbms_output.put_line('輸入無效');
RETURN;
END IF;
LOOP
FETCH c INTO v_name;
EXIT WHEN c%NOTFOUND;
dbms_output.put_line(v_name);
END LOOP;
CLOSE c;
END;
