可以设置一个封装查询的游标,然后一次读取几行查询结果,而不是立即执行整个查询。这样做的一个原因是避免在结果包含大量行时出现内存溢出。(然而,pl/sql用户通常不需要担心这个问题,因为FOR
循环在内部自动使用游标以避免内存问题。)更有趣的用法是返回一个函数创建的游标的引用,允许调用者读取行。这提供了一种从函数返回大型行集的有效方式。
游标是指向存储有关处理特定SELECT
或DML语句的信息的私有SQL区域的指针。
由pl/sql构建和管理的游标是隐式游标。您构建和管理的游标是显式游标。您可以从其属性获取有关任何会话游标的信息(您可以在过程语句中引用这些属性,但不能在SQL语句中引用)。
隐式游标是由pl/sql构建和管理的会话游标。每次运行SELECT
或DML语句时,pl/sql都会打开一个隐式游标。您无法控制隐式游标,但可以从其属性获取信息。
隐式游标将SELECT
查询的结果集给一个RECORD
变量,
这个变量名除了支持identifier
标识符以外,新增支持两个关键字INNER
和OUTER
。
举例,隐式游标获取结果集的RECORD
变量名为INNER
:
create table record_filed(id int, zqdm varchar(100)); insert into record_filed values(12,'hello world!'); DECLARE CURSOR c_a IS select * from record_filed; zqdm_p record_filed.id%TYPE; BEGIN FOR inner IN c_a LOOP zqdm_p := inner.id; end loop; end; /
举例,隐式游标获取结果集的RECORD
变量名为OUTER
:
create table record_filed(id int, zqdm varchar(100)); insert into record_filed values(12,'hello world!'); DECLARE CURSOR c_a IS select * from record_filed; zqdm_p record_filed.id%TYPE; BEGIN FOR outer IN c_a LOOP zqdm_p := outer.id; end loop; end; /
值得注意的是:
OUTER
为非保留关键字,可以作为表名、列名、函数名、类型名等,
INNER
为函数名类型名关键字,只能作为函数名;
类型名(具体指联合数组、可变数组和嵌套表)和FOR
循环变量的名字相同时,报错;
已创建 OUTER
同名的对象后,
不允许再创建OUTER
类型。
与之关联的语句运行后,隐式游标会关闭;但是,其属性值仍然可用,直到运行另一个SELECT
或DML语句。
最近运行的SELECT
或DML语句可能处于不同的作用域中。为了保存属性值以供以后使用,请立即将其分配给本地变量。否则,其他操作,例如子程序调用,可能会在您测试之前更改属性的值。
SQL%ISOPEN
属性:游标是否打开?
SQL%ISOPEN
总是返回FALSE
,因为一个隐式游标在关联的语句执行完后总是会关闭。
SQL%FOUND
属性:是否有任何行受到影响?
SQL%FOUND
返回:
如果没有SELECT
或DML语句运行,则返回NULL
,
如果SELECT
语句返回了一个或多个行或DML语句影响了一个或多个行,则返回TRUE
,
否则返回FALSE
。
使用 SQL%FOUND
来确定 DELETE
语句是否影响了任何行。例如:
create table employees(salary int, job_id varchar(100), employee_id int, last_name varchar(100), first_name varchar(100), department_id int, hire_date date); create table departments(department_id int, department_name varchar(100), location_id int, staff int); create table locations(country_id varchar(100),city varchar(100),location_id int); create table jobs(job_title varchar(100), job_id varchar(100)); insert into employees values (12308, 'SA_REP', 100, 'huaxiang', 'zhao', 2345, TO_DATE('31-DEC-2005', 'DD-MON-YYYY')); insert into employees values (12301, 'AD_FG', 12301, 'huahai', 'zhao', 2345, TO_DATE('31-DEC-2004', 'DD-MON-YYYY')); insert into employees values (12302, 'SA_REP', 12302, 'huaduo', 'zhao', 2345, TO_DATE('31-DEC-2004', 'DD-MON-YYYY')); insert into employees values (12302, 'S[HT]_CLERK', 12302, 'huashuai', 'zhao', 2345, TO_DATE('31-DEC-2004', 'DD-MON-YYYY')); insert into employees values (12302, 'AD_FG', 100, 'huashuai', 'zhao', 2345, TO_DATE('31-DEC-2004', 'DD-MON-YYYY')); insert into employees values (12304, 'AD_FG', 100, 'huashuai', 'zhao', 2345, TO_DATE('31-DEC-2004', 'DD-MON-YYYY')); insert into employees values (12305, 'AD_FG', 100, 'huashuai', 'zhao', 2345, TO_DATE('31-DEC-2004', 'DD-MON-YYYY')); insert into employees values (12306, 'AD_FG', 100, 'huashuai', 'zhao', 2345, TO_DATE('31-DEC-2004', 'DD-MON-YYYY')); insert into employees values (12307, 'AD_FG', 100, 'huashuai', 'zhao', 2345, TO_DATE('31-DEC-2004', 'DD-MON-YYYY')); insert into employees values (12308, 'AD_MAN', 100, 'huaxiang', 'zhao', 2345, TO_DATE('31-DEC-2004', 'DD-MON-YYYY')); insert into departments values (110, 'asd', 1700, 2345); insert into departments values (2345, 'asd', 1800, 2345); insert into departments values (110, 'asd', 1900, 2345); insert into departments values (110, 'AD_FG', 12302, 2345); insert into locations values ('JP', 'Japan', 1700); insert into locations values ('Toronto', 'Canada', 1800); insert into locations values ('WH', 'Whitehorse', 1900); insert into jobs values ('sdadf', 'AD_FG'); DROP TABLE dept_temp; CREATE TABLE dept_temp AS SELECT * FROM departments; DECLARE dept_no NUMBER := 12; BEGIN DELETE FROM dept_temp WHERE department_id = dept_no; IF SQL%FOUND THEN DBMS_OUTPUT.PUT_LINE ('Delete succeeded for department number ' || dept_no); ELSE DBMS_OUTPUT.PUT_LINE ('No department number ' || dept_no); END IF; END; /
SQL%NOTFOUND
Attribute: Were Any Rows Affected?
SQL%NOTFOUND
(逻辑上与 SQL%FOUND
相反)返回:
如果没有运行 SELECT
或 DML 语句,则返回 NULL
。
如果 SELECT
语句返回了一行或多行,或者 DML 语句影响了一行或多行,则返回 FALSE
。
SQL%ROWCOUNT
属性:有多少行受到影响?
SQL%ROWCOUNT
返回:
如果没有运行 SELECT
或 DML 语句,则返回 NULL
。
否则,返回 SELECT
语句返回的行数或被 DML 语句影响的行数(一个 INTEGER
类型)。
使用 SQL%ROWCOUNT
来确定被删除的行数。例如:
DROP TABLE employees_temp; CREATE TABLE employees_temp AS SELECT * FROM employees; DECLARE mgr_no int := 123005; rowcount int := 0; BEGIN DELETE FROM employees_temp WHERE employee_id = mgr_no; rowcount := SQL%ROWCOUNT; DBMS_OUTPUT.PUT_LINE ('Number of employees deleted: ' || rowcount); END; /
显式游标是一种会话游标,由您构造和管理。 您必须声明和定义一个显式游标,给它起一个名称并将其与一个查询关联(通常,查询会返回多行)。 然后,您可以通过以下两种方式之一处理查询结果集:
Open
显式游标,从结果集中 fetch
行,并 close
显式游标。
在游标 FOR LOOP
语句中使用显式游标。
与 隐式游标 不同,您可以通过名称引用 显式游标 或游标变量。因此,显式游标或游标变量被称为命名游标。
您可以先声明显式游标,然后在同一块、子程序或包中稍后定义它,也可以同时声明和定义它。
显式游标声明
,仅声明游标,其语法如下:
CURSOR cursor_name parameter_list
RETURN return_type;
显式游标定义
的语法如下:
CURSOR cursor_nameparameter_list
RETURN return_type
IS select_statement;
要实际使用显式游标
,可以直接定义它而不声明它。
在下面的示例中,声明并定义了三个显式游标:
DECLARE CURSOR c1 RETURN departments%ROWTYPE; -- 声明 c1 CURSOR c2 IS -- 声明并定义 c2 SELECT employee_id, job_id, salary FROM employees WHERE salary > 2000; CURSOR c1 RETURN departments%ROWTYPE IS -- 定义 c1, SELECT * FROM departments -- 重复返回类型 WHERE department_id = 110; CURSOR c3 RETURN locations%ROWTYPE; -- 声明 c3 CURSOR c3 IS -- 定义 c3, SELECT * FROM locations -- 省略返回类型 WHERE country_id = 'JP'; BEGIN NULL; END; /
声明并定义了显式游标后,可以使用OPEN语句打开它。游标名忽略大小写。 定义的游标不带参数,打开时也可以使用游标名加()的形式。
下面是打开游标的几种方式:
open C_tmp; open c_tmp; open c_tmp();
使用CLOSE
语句关闭打开的显式游标,从而允许重用其资源。关闭游标后,无法从其结果集中获取记录或引用其属性。
打开显式游标后,可以使用FETCH
语句获取查询结果集的行。返回一行的FETCH
语句的基本语法如下:
FETCH cursor_name INTO into_clause;
into_clause
是变量列表或单个记录变量。
对于查询返回的每一列,变量列表或记录必须有一个相应的类型兼容变量或字段。
在声明用于FETCH
语句的变量和记录时,%TYPE
和%ROWTYPE
属性非常有用。
FETCH
语句检索结果集的当前行,将该行的列值存储到变量或记录中,并将游标移动到下一行。
通常,您在LOOP
语句中使用FETCH
语句,当 FETCH
语句运行完所有行时退出循环。
要检测此退出条件,请使用游标属性%NOTFOUND
。当FETCH
语句返回没有行时,PL/oraSQL不会引发异常。
使用FETCH
和%NOTFOUND在LOOP
语句中一次一行地检索两个显式游标的结果集。
第一个FETCH
语句将列值检索到变量中,第二个FETCH
语句将列值检索到记录中。
变量和记录分别使用%TYPE
和%ROWTYPE
声明。例如:
DECLARE CURSOR c1 IS SELECT last_name, job_id FROM employees WHERE REGEXP_LIKE (job_id, 'S[HT]_CLERK') ORDER BY last_name; v_lastname employees.last_name%TYPE; -- variable for last_name v_jobid employees.job_id%TYPE; -- variable for job_id CURSOR c2 IS SELECT * FROM employees WHERE REGEXP_LIKE (job_id, '[ACADFIMKSA]_M[ANGR]') ORDER BY job_id; v_employees employees%ROWTYPE; -- record variable for row of table BEGIN OPEN c1; LOOP -- Fetches 2 columns into variables FETCH c1 INTO v_lastname, v_jobid; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE (RPAD(v_lastname, 25, ' ') || v_jobid); END LOOP; CLOSE c1; DBMS_OUTPUT.PUT_LINE ('-------------------------------------'); OPEN c2; LOOP -- Fetches entire row into the v_employees record FETCH c2 INTO v_employees; EXIT WHEN c2%NOTFOUND; DBMS_OUTPUT.PUT_LINE (RPAD(v_employees.last_name, 25, ' ') || v_employees.job_id); END LOOP; CLOSE c2; END; /
这个操作通过使用五个FETCH
语句,
将结果集的前五行数据分别装载到五个记录变量中。
这些记录变量使用%ROWTYPE
进行声明。例如:
DECLARE CURSOR c IS SELECT e.job_id, j.job_title FROM employees e, jobs j WHERE e.job_id = j.job_id AND e.employee_id = 100 ORDER BY last_name; -- Record variables for rows of cursor result set: job1 c%ROWTYPE; job2 c%ROWTYPE; job3 c%ROWTYPE; job4 c%ROWTYPE; job5 c%ROWTYPE; BEGIN OPEN c; FETCH c INTO job1; -- fetches first row FETCH c INTO job2; -- fetches second row FETCH c INTO job3; -- fetches third row FETCH c INTO job4; -- fetches fourth row FETCH c INTO job5; -- fetches fifth row CLOSE c; DBMS_OUTPUT.PUT_LINE (job1.job_title || ' (' || job1.job_id || ')'); DBMS_OUTPUT.PUT_LINE (job2.job_title || ' (' || job2.job_id || ')'); DBMS_OUTPUT.PUT_LINE (job3.job_title || ' (' || job3.job_id || ')'); DBMS_OUTPUT.PUT_LINE (job4.job_title || ' (' || job4.job_id || ')'); DBMS_OUTPUT.PUT_LINE (job5.job_title || ' (' || job5.job_id || ')'); END; /
显式游标查询可以引用其作用域内的任何变量。 当您打开显式游标时,PL/oraSQL会评估查询中的任何变量,并在识别结果集时使用这些值。 后来更改变量的值不会改变结果集。
显式游标查询引用变量factor
。
当游标打开时,factor
的值为2。因此,
sal_multiple
始终为sal
的两倍,
尽管在每次检索后都会增加factor
的值。例如:
DECLARE sal employees.salary%TYPE; sal_multiple employees.salary%TYPE; factor INTEGER := 2; CURSOR c1 IS SELECT salary, salary*factor FROM employees WHERE job_id LIKE 'AD_%'; BEGIN OPEN c1; -- PL/SQL evaluates factor LOOP FETCH c1 INTO sal, sal_multiple; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE ('factor = ' || factor); DBMS_OUTPUT.PUT_LINE ('sal = ' || sal); DBMS_OUTPUT.PUT_LINE ('sal_multiple = ' || sal_multiple); factor := factor + 1; -- Does not affect sal_multiple END LOOP; CLOSE c1; END; /
显式游标查询中的变量-结果集更改。例如:
DECLARE sal employees.salary%TYPE; sal_multiple employees.salary%TYPE; factor INTEGER := 2; CURSOR c1 IS SELECT salary, salary*factor FROM employees WHERE job_id LIKE 'AD_%'; BEGIN DBMS_OUTPUT.PUT_LINE ('factor = ' || factor); OPEN c1; -- PL/SQL evaluates factor LOOP FETCH c1 INTO sal, sal_multiple; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE ('sal = ' || sal); DBMS_OUTPUT.PUT_LINE ('sal_multiple = ' || sal_multiple); END LOOP; CLOSE c1; factor := factor + 1; DBMS_OUTPUT.PUT_LINE ('factor = ' || factor); OPEN c1; -- PL/SQL evaluates factor LOOP FETCH c1 INTO sal, sal_multiple; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE ('sal = ' || sal); DBMS_OUTPUT.PUT_LINE ('sal_multiple = ' || sal_multiple); END LOOP; CLOSE c1; END; /
当显式游标查询包括虚拟列(表达式)时, 如果符合以下任一条件,则该列必须有别名:
使用CLOSE
语句可以关闭一个开启的显式游标,
从而允许其资源被重用。关闭游标之后,您不能再从其结果集中获取记录或引用其属性。
如果尝试这样做,PL/oraSQL会引发预定义的异常INVALID_CURSOR
。
如果您使用的游标是用%ROWTYPE
声明的记录变量,则可以使用该游标将数据装载到该记录变量中。
如果您想在程序中引用虚拟列,则可以使用该游标。
显式游标中的虚拟列需要为上述两个原因都提供别名。例如:
DECLARE CURSOR c1 IS SELECT employee_id, (salary * .05) raise FROM employees WHERE job_id LIKE '%_MAN' ORDER BY employee_id; emp_rec c1%ROWTYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO emp_rec; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE ('Raise for employee #' || emp_rec.employee_id || ' is $' || emp_rec.raise); END LOOP; CLOSE c1; END; /
您可以创建具有形式参数的显式游标, 然后每次打开游标时传递不同的实际参数。 在游标查询中,您可以在可以使用常量的任何位置使用形式游标参数。 在游标查询之外,您无法引用形式游标参数。
创建一个显式游标,其两个形式参数表示一个工作和其最高工资。 当使用指定的工作和最高工资打开游标时, 游标查询会选择那些薪资过高的职员(对于每个这样的职员,查询会选择其名字、姓氏和超支金额)。 接下来,该示例创建一个打印游标查询结果集的过程(关于过程的信息,请参阅PL/SQL子程序)。 最后,该示例使用一组实际参数打开游标,打印结果集,关闭游标,然后使用不同的实际参数打开游标,打印结果集并关闭游标。例如:
DECLARE CURSOR c (job VARCHAR2, max_sal NUMBER) IS SELECT last_name, first_name, (salary - max_sal) overpayment FROM employees WHERE job_id = job AND salary > max_sal ORDER BY salary; last_name_ employees.last_name%TYPE; first_name_ employees.first_name%TYPE; overpayment_ employees.salary%TYPE; BEGIN DBMS_OUTPUT.PUT_LINE ('----------------------'); DBMS_OUTPUT.PUT_LINE ('Overpaid Stock Clerks:'); DBMS_OUTPUT.PUT_LINE ('----------------------'); OPEN c('ST_CLERK', 5000); LOOP FETCH c INTO last_name_, first_name_, overpayment_; EXIT WHEN c%NOTFOUND; DBMS_OUTPUT.PUT_LINE (last_name_ || ',' || first_name_ || ', (by' || overpayment_ || ')'); END LOOP; CLOSE c; DBMS_OUTPUT.PUT_LINE ('----------------------'); DBMS_OUTPUT.PUT_LINE ('Overpaid Sales Representatives:'); DBMS_OUTPUT.PUT_LINE ('----------------------'); OPEN c('SA_REP', 10000); LOOP FETCH c INTO last_name_, first_name_, overpayment_; EXIT WHEN c%NOTFOUND; DBMS_OUTPUT.PUT_LINE (last_name_ || ',' || first_name_ || ', (by' || overpayment_ || ')'); END LOOP; CLOSE c; END; /
当您创建一个具有形式参数的显式游标时, 您可以为它们指定默认值。当形式参数具有默认值时, 相应的实际参数是可选的。 如果您在不指定实际参数的情况下打开游标, 那么形式参数具有默认值。
parameter_name [IN] data_type [:= | default expression
]
应用场景包括存储过程、函数、匿名块和包。
与Oracle相比,有一些差异。例如,在 data_type
中,
Oracle 支持在游标参数字符串中声明 char,
但是 lightdb 的 char 长度默认值为 1,
因此对于长度超过 1 的字符串,您需要指定 char 长度,char(100)。
但对于可变长度字符类型,如 varchar、varchar2 等,使用方式与 Oracle 相同。
并且不支持 Oracle 类型:string、long、raw、long raw、rowid、timestamp with local time 等。
在包中不支持由 constant 定义的常量类型。
与 Oracle 相比,Expressions
也有一些差异。
布尔表达式
包支持返回布尔类型、布尔表达式和布尔字面量的函数(支持 NULL,Oracle 不支持),
函数、存储过程和匿名块支持返回布尔类型、布尔常量、布尔变量、布尔表达式和布尔字面量的函数(支持 NULL,Oracle 不支持)。
字符表达式
包支持返回 characer 类型和 characer 字面量的函数,
函数、存储过程和匿名块支持返回 characer 类型、characer 常量、characer变量、characer 表达式和 characer 字面量的函数。
日期表达式
包支持返回 date 类型和 date 字面量的函数,
函数、存储过程和匿名块支持返回 date 类型、date 常量、date 变量、date 表达式和 date 字面量的函数。
数值表达式
包中不支持:collection、named_cursor、numberic_constant、numberic_variable、placeholder、SQL% cursor attribute、**(不支持的运算符),
函数、存储过程和匿名块中不支持:collection、named_cursor、placeholder、SQL% cursor attribute、**(不支持的运算符)。
Case 表达式
包、函数、存储过程和匿名块中支持 search_case_expression 和 simple_case_expression。
创建一个显式游标,其形式参数代表位置ID。参数的默认值是公司总部的位置ID。例如:
DECLARE CURSOR c (location NUMBER DEFAULT 1700) IS SELECT d.department_name, e.last_name manager, l.city FROM departments d, employees e, locations l WHERE l.location_id = location AND l.location_id = d.location_id AND d.department_id = e.department_id ORDER BY d.department_id; dept_name departments.department_name%TYPE; mgr_name employees.last_name%TYPE; city_name locations.city%TYPE; BEGIN DBMS_OUTPUT.PUT_LINE ('DEPARTMENTS AT HEADQUARTERS:'); DBMS_OUTPUT.PUT_LINE ('--------------------------------'); OPEN c; LOOP FETCH c INTO dept_name, mgr_name, city_name; EXIT WHEN c%NOTFOUND; DBMS_OUTPUT.PUT_LINE (dept_name || ' (Manager: ' || mgr_name || ')'); END LOOP; DBMS_OUTPUT.PUT_LINE ('--------------------------------'); CLOSE c; DBMS_OUTPUT.PUT_LINE ('DEPARTMENTS IN CANADA:'); DBMS_OUTPUT.PUT_LINE ('--------------------------------'); OPEN c(1800); -- Toronto LOOP FETCH c INTO dept_name, mgr_name, city_name; EXIT WHEN c%NOTFOUND; DBMS_OUTPUT.PUT_LINE (dept_name || ' (Manager: ' || mgr_name || ')'); END LOOP; CLOSE c; OPEN c(1900); -- Whitehorse LOOP FETCH c INTO dept_name, mgr_name, city_name; EXIT WHEN c%NOTFOUND; DBMS_OUTPUT.PUT_LINE (dept_name || ' (Manager: ' || mgr_name || ')'); END LOOP; CLOSE c; END; /
如果你向游标添加形式参数,并为这些参数指定了默认值,那么您无需更改对游标的现有引用。例如:
DECLARE CURSOR c (job VARCHAR2, max_sal NUMBER, hired DATE DEFAULT TO_DATE('31-DEC-1999', 'DD-MON-YYYY')) IS SELECT last_name, first_name, (salary - max_sal) overpayment FROM employees WHERE job_id = job AND salary > max_sal AND hire_date > hired ORDER BY salary; last_name_ employees.last_name%TYPE; first_name_ employees.first_name%TYPE; overpayment_ employees.salary%TYPE; BEGIN DBMS_OUTPUT.PUT_LINE ('--------------------------------'); DBMS_OUTPUT.PUT_LINE ('Overpaid Sales Representatives Hired After 2004:'); DBMS_OUTPUT.PUT_LINE ('--------------------------------'); OPEN c('SA_REP', 10000); LOOP FETCH c INTO last_name_, first_name_, overpayment_; EXIT WHEN c%NOTFOUND; DBMS_OUTPUT.PUT_LINE (last_name_ || ', (by ' || overpayment_ || ')'); END LOOP; CLOSE c; END; /
显式游标属性的语法是cursor_name
,紧跟着属性(例如,c1%ISOPEN
)。
使用CLOSE
语句关闭一个打开的显式游标,从而允许其资源被重用。
关闭游标之后,您就无法从其结果集中提取记录或引用其属性。
如果尝试这样做,PL/oraSQL会引发预定义的异常INVALID_CURSOR
。
%ISOPEN
属性:游标是否打开?
如果显式游标处于打开状态,%ISOPEN
会返回true
,否则返回false
。
仅当显式游标c1
未打开时,才打开它;仅当它已打开时,才关闭它。例如:
DECLARE CURSOR c1 IS SELECT last_name, salary FROM employees WHERE ROWNUM < 11; the_name employees.last_name%TYPE; the_salary employees.salary%TYPE; BEGIN IF NOT c1%ISOPEN THEN OPEN c1; END IF; FETCH c1 INTO the_name, the_salary; IF c1%ISOPEN THEN CLOSE c1; END IF; END; /
要使用ROWNUM,lightdb_dblevel_syntax_compatible_type的值必须为oracle。
%FOUND
属性:是否有行受影响?
%FOUND
返回:
在显式游标打开但在第一次提取之前返回NULL
,
如果最近一次从显式游标提取了一行,则返回TRUE
,
否则返回FALSE
。
循环遍历结果集,打印每个提取的行,并在没有更多行可提取时退出。例如:
DECLARE CURSOR c1 IS SELECT last_name, salary FROM employees WHERE ROWNUM < 11 ORDER BY last_name; my_ename employees.last_name%TYPE; my_salary employees.salary%TYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO my_ename, my_salary; IF c1%FOUND THEN -- fetch succeeded DBMS_OUTPUT.PUT_LINE ('Name = ' || my_ename || ' salary = ' || my_salary); ELSE -- fetch failed EXIT; END IF; END LOOP; END; /
%NOTFOUND
属性:是否有行受影响?
%NOTFOUND
(the logical opposite of %FOUND
) returns:
在显式游标打开但在第一次提取之前返回NULL
,
如果最近一次从显式游标提取了一行,则返回FALSE
,
否则返回TRUE
。
当FETCH
未返回行时,%NOTFOUND
用于退出循环。例如:
DECLARE CURSOR c1 IS SELECT last_name FROM employees WHERE ROWNUM < 11 ORDER BY last_name; rowcount int; name_1 employees.last_name%TYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO name_1; EXIT WHEN c1%NOTFOUND; rowcount := c1%ROWCOUNT; DBMS_OUTPUT.PUT_LINE (rowcount ||'. ' || name_1); IF c1%ROWCOUNT = 5 THEN DBMS_OUTPUT.PUT_LINE ('--- Fetched 5th row ---'); END IF; END LOOP; CLOSE c1; END; /
%ROWCOUNT
属性:有多少行受到影响?
%ROWCOUNT
returns:
在显式游标打开但在第一次提取之前返回零,
否则,返回提取的行数(一个INTEGER
)。
对提取的行进行编号并打印,并在提取第五行后打印一条消息。例如:
DECLARE CURSOR c1 IS SELECT last_name FROM employees WHERE ROWNUM < 11 ORDER BY last_name; name_1 employees.last_name%TYPE; rowcount int; BEGIN OPEN c1; LOOP FETCH c1 INTO name_1; EXIT WHEN c1%NOTFOUND OR c1%NOTFOUND IS NULL; rowcount := c1%ROWCOUNT; DBMS_OUTPUT.PUT_LINE (rowcount || '. ' || name_1); IF c1%ROWCOUNT = 5 THEN DBMS_OUTPUT.PUT_LINE ('--- Fetched 5th row ---'); END IF; END LOOP; CLOSE c1; END; /
在PL/oraSQL中,与传统的数据库编程一样,您使用游标来处理查询结果集。 然而,在PL/oraSQL中,您可以使用隐式游标或显式游标。
前者需要的代码较少,但后者更加灵活。 例如,显式游标可以接受参数。
以下 pl/sql 语句使用 pl/sql 为您定义和管理的隐式游标:
SELECT INTO
。
隐式游标 FOR LOOP
。
以下 pl/sql 语句使用显式游标:
显式游标 FOR LOOP
。
OPEN
、FETCH
和 CLOSE
。
使用隐式游标,SELECT INTO
语句从一个或多个数据库表中检索值(就像SQL的SELECT
语句一样)并将它们存储在变量中(这是SQL的SELECT
语句所不具备的)。
如果您希望查询仅返回一行,则使用SELECT INTO
语句将该行的值存储在一个或多个标量变量,或一个记录变量中。
如果查询可能返回多行,但您只关心第n行,则可以使用WHERE ROWNUM=n
子句将结果集限制为该行。
如果您必须将大量的表数据分配给变量,LightDB建议使用带有BULK COLLECT
子句的SELECT INTO
语句。
游标FOR LOOP
语句允许您运行SELECT
语句,然后立即循环遍历结果集中的行。
这个语句可以使用隐式或显式游标(但不能使用游标变量)。
如果您只在游标的 FOR LOOP 语句中使用SELECT
语句,
则应该在游标的 FOR LOOP 语句内部指定SELECT
语句。
这称为隐式游标的FOR LOOP
语句。
例如:
BEGIN FOR item IN ( select last_name, job_id FROM employees WHERE job_id LIKE '%CLERK%' AND employee_id > 120 ) LOOP DBMS_OUTPUT.PUT_LINE ('Name = ' || item.last_name || ', Job = ' || item.job_id); END LOOP; END; /
为了兼容Oracle数据库,LightDB支持在隐式游标FOR LOOP
语句中使用WITH
子句。
例如:
CREATE TABLE employees (employee_id NUMBER PRIMARY KEY, first_name VARCHAR2(50)); INSERT INTO employees VALUES (1, 'John'); INSERT INTO employees VALUES (2, 'Jane'); INSERT INTO employees VALUES (3, 'Bob'); BEGIN FOR emp_rec IN (WITH employeetemp AS ( SELECT employee_id AS eid, first_name AS fname FROM employees ) SELECT eid,fname FROM employeetemp ) loop DBMS_OUTPUT.PUT_LINE('Employee ID: ' || emp_rec.eid || ', Name: ' || emp_rec.fname); END LOOP; END; /
显式游标FOR LOOP
语句。例如:
DECLARE CURSOR c1 IS SELECT last_name, job_id FROM employees WHERE job_id LIKE '%CLERK%' AND employee_id > 120 ORDER BY last_name; BEGIN FOR item IN c1 LOOP DBMS_OUTPUT.PUT_LINE ('Name = ' || item.last_name || ', Job = ' || item.job_id); END LOOP; END; /
传递参数给显式游标FOR LOOP语句。例如:
DECLARE CURSOR c1 (job VARCHAR2, max_wage NUMBER) IS SELECT * FROM employees WHERE job_id = job AND salary > max_wage; BEGIN FOR person IN c1('ST_CLERK', 3000) LOOP -- 处理数据记录 DBMS_OUTPUT.PUT_LINE ('Name = ' || person.last_name || ', salary = ' || person.salary || ', Job id= ' || person.job_id); END LOOP; END; /
游标FOR LOOP
引用虚拟列。例如:
BEGIN FOR item IN ( SELECT first_name || ' ' || last_name AS full_name, salary * 10 AS dream_salary FROM employees WHERE ROWNUM < 6 ORDER BY dream_salary DESC, last_name ASC ) LOOP DBMS_OUTPUT.PUT_LINE (item.full_name || ' dreams of making ' || item.dream_salary); END LOOP; END; /
如果你通过循环遍历查询结果集并为每行运行另一个查询来处理结果集, 那么你可以通过将第二个查询从循环内部删除并将其作为第一个查询的子查询来提高性能。
虽然普通的子查询对每个表进行评估,
但相关子查询
对每一行进行评估。
父查询中FROM
子句中的子查询。例如:
DECLARE CURSOR c1 IS SELECT t1.department_id, t1.department_name, t2.staff FROM departments t1, ( SELECT department_id, COUNT(*) AS staff FROM employees GROUP BY department_id ) t2 WHERE (t1.department_id = t2.department_id) AND t2.staff > 5 ORDER BY t2.staff; BEGIN FOR dept IN c1 LOOP DBMS_OUTPUT.PUT_LINE ('Department = ' || dept.department_name || ', staff = ' || dept.staff); END LOOP; END; /
相关子查询。例如:
DECLARE CURSOR c1 IS SELECT department_id, last_name, salary FROM employees t WHERE salary >= ( SELECT AVG(salary) FROM employees WHERE t.department_id = department_id ) ORDER BY department_id, last_name; BEGIN FOR person IN c1 LOOP DBMS_OUTPUT.PUT_LINE ('Making above-average salary = ' || person.last_name); END LOOP; END; /
cursor
变量类似于显式游标,但具有以下特点:
它不限于一个查询。 您可以为一个查询打开一个游标变量,处理结果集,然后将游标变量用于另一个查询。
您可以为它赋值。
您可以在表达式中使用它。
它可以是子程序参数。 您可以使用游标变量在子程序之间传递查询结果集。
它可以是宿主变量。 您可以使用游标变量在PL/oraSQL存储的子程序和它们的客户端之间传递查询结果集。
它无法接受参数。 您不能向游标变量传递参数,但可以将整个查询传递给它。 查询可以包括变量。
要创建游标变量,请声明一个预定义类型 SYS_REFCURSOR
的变量,
或者定义一个 REF CURSOR
类型,然后声明一个该类型的变量。
REF CURSOR
类型定义的基本语法如下:
TYPE type_name IS REF CURSOR RETURN return_type
%TYPE
游标变量声明。例如:
DECLARE TYPE empcurtyp IS REF CURSOR RETURN employees%ROWTYPE; TYPE genericcurtyp IS REF CURSOR; cursor1 empcurtyp; cursor2 genericcurtyp; my_cursor SYS_REFCURSOR; TYPE deptcurtyp IS REF CURSOR RETURN departments%ROWTYPE; dept_cv deptcurtyp; -- strong cursor variable BEGIN NULL; END; /
具有用户定义返回类型的游标变量。例如:
DECLARE TYPE EmpRecTyp IS RECORD ( employee_id NUMBER, last_name VARCHAR2(25), salary NUMBER(8,2)); TYPE EmpCurTyp IS REF CURSOR RETURN EmpRecTyp; emp_cv EmpCurTyp; BEGIN NULL; END; /
使用 CREATE DOMAIN
创建的游标类型的变量。例如:
CREATE DOMAIN mycursor AS refcursor; SELECT dbms_output.serveroutput(true); DECLARE employee_cursor mycursor; v_employee_name VARCHAR2(50); BEGIN OPEN employee_cursor for SELECT first_name FROM employees_curs WHERE employee_id = 1500; LOOP FETCH employee_cursor INTO v_employee_name; EXIT WHEN employee_cursor%NOTFOUND; DBMS_OUTPUT.PUT_LINE('Employee Name: ' || v_employee_name); END LOOP; CLOSE employee_cursor; END; /
使用sys_refcursor
作为参数变量。例如:
CREATE TABLE customer_address ( ca_address_sk integer not null, ca_address_id char(16) not null, ca_street_number char(10) , ca_street_name varchar(60) , ca_street_type char(15) , ca_suite_number char(10) , ca_city varchar(60) , ca_county varchar(30) , ca_state char(2) , ca_zip char(10) , ca_country varchar(20) , ca_gmt_offset decimal(5,2) , ca_location_type char(20) , PRIMARY KEY (ca_address_sk) ); DECLARE i_id integer := 1; address_id char(16); street_number char(10); street_name varchar(60) ; street_type char(15); suite_number char(10); city varchar(60); county varchar(30); state char(2); zip char(10); country varchar(20); gmt_offset decimal(5,2); location_type char(20); BEGIN -- insert address_id := 'address_id'; street_number := '3588' ; street_name := 'street_name'; street_type := 'street_type'; suite_number := '1'; city := 'Hang Zhou'; county := 'Bin Jiang'; state := 'CN'; zip := '000000'; country := 'China'; gmt_offset := 1.02; location_type := 'location_type'; INSERT INTO customer_address(ca_address_sk, ca_address_id, ca_street_number, ca_street_name, ca_street_type, ca_suite_number, ca_city, ca_county, ca_state, ca_zip, ca_country, ca_gmt_offset, ca_location_type) VALUES (i_id, address_id, street_number, street_name, street_type, suite_number, city, county, state, zip, country, gmt_offset, location_type); END; / CREATE OR REPLACE PROCEDURE getCustomerAddress(cur OUT sys_refcursor) IS BEGIN OPEN cur FOR SELECT * FROM customer_address; END; / DECLARE ad sys_refcursor; res customer_address%rowtype; BEGIN getCustomerAddress(ad); LOOP FETCH ad INTO res; EXIT WHEN ad%NOTFOUND; dbms_output.put_line('ca_address_sk:' || res.ca_address_sk); dbms_output.put_line('ca_address_id:' || res.ca_address_id); dbms_output.put_line('ca_street_number:' || res.ca_street_number); dbms_output.put_line('ca_street_name:' || res.ca_street_name); dbms_output.put_line('ca_street_type:' || res.ca_street_type); dbms_output.put_line('ca_suite_number:' || res.ca_suite_number); dbms_output.put_line('ca_city:' || res.ca_city); dbms_output.put_line('ca_county:' || res.ca_county); dbms_output.put_line('ca_state:' || res.ca_state); dbms_output.put_line('ca_zip:' || res.ca_zip); dbms_output.put_line('ca_country:' || res.ca_country); dbms_output.put_line('ca_gmt_offset:' || res.ca_gmt_offset); dbms_output.put_line('ca_location_type:' || res.ca_location_type); END LOOP; END; /
您可以使用 OPEN FOR
语句打开游标变量,该语句执行以下操作:
将游标变量与查询关联(通常情况下,查询返回多行)。
查询可以包含绑定变量的占位符,在 OPEN FOR
语句的 USING
子句中指定其值。
分配数据库资源以处理查询。
处理查询,即:
识别结果集。 如果查询引用变量,则它们的值会影响结果集。
如果查询具有 FOR UPDATE
子句,则锁定结果集中的行。
将游标定位在结果集的第一行之前。
在for
循环中,将select
查询的结果给一个record
类型,
这一操作也被称为隐式游标操作。
record
类型中一个字段用来接收查询结果中的一个select
查询语句
(update
,delete
,insert
在这个语法中都会报错),
这个字段被用作open for
动态打开游标的对象。
LightDB不支持包括以下两点:
FOR
和IN
中间的格式可以为A或者A.B格式。
举例,FOR
和IN
中间的格式可以为A:
select dbms_output.serveroutput('t'); create table employees(salary int, job_id varchar2(100), employee_id int, last_name varchar(100), first_name varchar(100), department_id int, hire_date date); create table V2_text ( object_name VARCHAR2(40), object_id VARCHAR2(8), sql_text VARCHAR2(1000) ); create table orders (id int,org_id int,user_id int ,order_id int,customer_id int, order_date date, price int ,primary key (org_id, id),status varchar(20)); insert into employees values (12308, 'SA_REP', 100, 'huaxiang', 'zhao', 10, TO_DATE('31-DEC-2005', 'DD-MON-YYYY')); insert into employees values (12301, 'AD_FG', 12301, 'huahai', 'zhao', 10, TO_DATE('31-DEC-2004', 'DD-MON-YYYY')); insert into employees values (12302, 'SA_REP', 12302, 'huaduo', 'zhao', 10, TO_DATE('31-DEC-2004', 'DD-MON-YYYY')); insert into orders values (12301, 100, 2346, 456043, 23490, TO_DATE('31-DEC-2005', 'DD-MON-YYYY'), 2000, 'Completed'); -- select insert into V2_text (object_name, object_id, sql_text) values ('EmployeeTable', 'OBJ0001', 'SELECT job_id FROM employees WHERE department_id = 10'); DECLARE type t_dynamic_cursor is ref cursor; dynamic_cur t_dynamic_cursor; type t_inter_code_table is table of varchar2(128); rec_all_code t_inter_code_table; BEGIN FOR rec IN ( SELECT object_name,object_id,sql_text FROM V2_text ) LOOP OPEN dynamic_cur FOR rec.sql_text; -- Perform cursor operations fetch dynamic_cur bulk collect into rec_all_code; for i in 1..rec_all_code.count loop DBMS_OUTPUT.PUT_LINE('OBJECTNAME: ' ||rec_all_code(i)); end loop; close dynamic_cur; END LOOP; END; /
举例,FOR
和IN
中间的格式可以为A.B:
declare type t_dynamic_cursor is ref cursor; dynamic_cur t_dynamic_cursor; type t_inter_code_table is table of varchar2(128); rec_all_code t_inter_code_table; type emp_rec_type is record (ename varchar2(25), sql_text varchar2(10), sal number(7,2) ); type emp_record_type is record (ename varchar2(25), job varchar2(10), sal number(7,2), rec emp_rec_type ); sys emp_record_type; BEGIN FOR sys.rec IN ( SELECT object_name,object_id,sql_text FROM V2_text ) LOOP NULL; END LOOP; END; /
FOR
之后的动态语句的格式,不支持多个复杂类型进行嵌套对变量的访问,只支持单个复杂类型。
举例,支持单个复杂类型对变量的访问:
DECLARE type t_dynamic_cursor is ref cursor; dynamic_cur t_dynamic_cursor; type t_inter_code_table is table of varchar2(128); rec_all_code t_inter_code_table; BEGIN FOR rec IN ( SELECT object_name,object_id,sql_text FROM V2_text ) LOOP OPEN dynamic_cur FOR rec.sql_text; -- Perform cursor operations fetch dynamic_cur bulk collect into rec_all_code; for i in 1..rec_all_code.count loop DBMS_OUTPUT.PUT_LINE('OBJECTNAME: ' ||rec_all_code(i)); end loop; close dynamic_cur; END LOOP; END; /
举例,不支持多个复杂类型进行嵌套对变量的访问:
DECLARE type t_dynamic_cursor is ref cursor; dynamic_cur t_dynamic_cursor; type t_inter_code_table is table of varchar2(128); rec_all_code t_inter_code_table; type emp_rec_type is record (ename varchar2(25), sql_text varchar2(100), sal number(7,2) ); type emp_record_type is record (ename varchar2(25), job varchar2(10), sal number(7,2), rec emp_rec_type ); type empl_record_type is record (ename varchar2(25), job varchar2(10), sal number(7,2), rec_emp emp_record_type ); sys empl_record_type; BEGIN FOR sys.rec_emp.rec IN ( SELECT object_name,object_id,sql_text FROM V2_text ) LOOP NULL; END LOOP; END; /
打开游标变量后,您可以使用 FETCH
语句获取查询结果集的行。
游标变量的返回类型必须与 FETCH
语句的 into_clause
兼容。
第一个OPEN FOR
语句包含查询本身。
第二个OPEN FOR
i语句引用一个变量,其值是一个查询。
例如:
DECLARE cv SYS_REFCURSOR; -- cursor variable v_lastname employees.last_name%TYPE; -- variable for last_name v_jobid employees.job_id%TYPE; -- variable for job_id query_2 VARCHAR2(200) := 'SELECT * FROM employees WHERE REGEXP_LIKE (job_id, ''[ACADFIMKSA]_M[ANGR]'') ORDER BY job_id'; v_employees employees%ROWTYPE; -- record variable row of table BEGIN OPEN cv FOR SELECT last_name, job_id FROM employees WHERE REGEXP_LIKE (job_id, 'S[HT]_CLERK') ORDER BY last_name; LOOP -- Fetches 2 columns into variables FETCH cv INTO v_lastname, v_jobid; EXIT WHEN cv%NOTFOUND; DBMS_OUTPUT.PUT_LINE (RPAD(v_lastname, 25, ' ') || v_jobid); END LOOP; DBMS_OUTPUT.PUT_LINE ('-------------------------------------'); CLOSE CV; OPEN cv FOR query_2; LOOP -- Fetches entire row into the v_employees record FETCH cv INTO v_employees; EXIT WHEN cv%NOTFOUND; DBMS_OUTPUT.PUT_LINE (RPAD(v_employees.last_name, 25, ' ') || v_employees.job_id); END LOOP; CLOSE cv; END; /
您可以将一个PL/oraSQL光标变量的值赋给另一个PL/oraSQL光标变量或主机光标变量。
target_cursor_variable := source_cursor_variable;
如果source_cursor_variable
是打开的,在赋值后,target_cursor_variable
也将是打开的。
这两个光标变量指向相同的SQL工作区。
如果source_cursor_variable
没有打开,在赋值后打开target_cursor_variable
并不会打开source_cursor_variable
。
从光标变量中提取数据到集合中。例如:
DECLARE TYPE empcurtyp IS REF CURSOR; emp_cv empcurtyp; emp_cf empcurtyp; item employees%rowtype; BEGIN OPEN emp_cv FOR SELECT * FROM employees WHERE job_id = 'SA_REP' ORDER BY salary DESC; OPEN emp_cf FOR SELECT * FROM employees; emp_cf := emp_cv; LOOP FETCH emp_cf INTO item; DBMS_OUTPUT.PUT_LINE ('Name = ' || item.last_name || ', salary = ' || item.salary); EXIT WHEN emp_cf%notfound; END LOOP; CLOSE emp_cf; END; /
与光标变量相关联的查询可以引用其作用域内的任何变量。
当您使用OPEN FOR语句打开光标变量时,PL/oraSQL将评估查询中的任何变量,并在识别结果集时使用这些值。稍后更改变量的值不会更改结果集。
从光标变量中提取数据到集合中。例如:
DECLARE sal employees.salary%TYPE; sal_multiple employees.salary%TYPE; factor INTEGER := 2; cv SYS_REFCURSOR; BEGIN OPEN cv FOR SELECT salary, salary*factor FROM employees WHERE job_id LIKE 'AD_%'; -- PL/SQL evaluates factor LOOP FETCH cv INTO sal, sal_multiple; EXIT WHEN cv%NOTFOUND; DBMS_OUTPUT.PUT_LINE ('factor = ' || factor); DBMS_OUTPUT.PUT_LINE ('sal = ' || sal); DBMS_OUTPUT.PUT_LINE ('sal_multiple = ' || sal_multiple); factor := factor + 1; -- Does not affect sal_multiple END LOOP; CLOSE cv; END; /
从光标变量中提取数据到集合中。例如:
DECLARE sal employees.salary%TYPE; sal_multiple employees.salary%TYPE; factor INTEGER := 2; cv SYS_REFCURSOR; BEGIN DBMS_OUTPUT.PUT_LINE ('factor = ' || factor); OPEN cv FOR SELECT salary, salary*factor FROM employees WHERE job_id LIKE 'AD_%'; -- PL/SQL evaluates factor LOOP FETCH cv INTO sal, sal_multiple; EXIT WHEN cv%NOTFOUND; DBMS_OUTPUT.PUT_LINE ('sal = ' || sal); DBMS_OUTPUT.PUT_LINE ('sal_multiple = ' || sal_multiple); END LOOP; factor := factor + 1; DBMS_OUTPUT.PUT_LINE ('factor = ' || factor); CLOSE cv; OPEN cv FOR SELECT salary, salary*factor FROM employees WHERE job_id LIKE 'AD_%'; -- PL/SQL evaluates factor LOOP FETCH cv INTO sal, sal_multiple; EXIT WHEN cv%NOTFOUND; DBMS_OUTPUT.PUT_LINE ('sal = ' || sal); DBMS_OUTPUT.PUT_LINE ('sal_multiple = ' || sal_multiple); END LOOP; CLOSE cv; END; /
光标变量具有与显式光标相同的属性(请参见显式光标属性)。光标变量属性的值的语法是光标变量名称后紧跟属性(例如,cv%ISOPEN)。 如果光标变量没有打开,则引用%ISOPEN以外的任何属性都会引发预定义的INVALID_CURSOR异常。