我们知道关系数据库管理系统实质是面向集合的,在MS SQL SERVER 中并没有一种描述表中单一记录的表达形式,除非使用where
子句来限制只有一条记录被选中。因此我们必须借助于游标来进行面向单条记录的数据处理。由此可见,游标允许应用程序对查询语句select
返回的行结果集中每一行进行相同或不同的操作,而不是一次对整个结果集进行同一种操作;它还提供对基于游标位置而对表中数据进行删除或更新的能力;而且,正是游标把作为面向集合的数据库管理系统和面向行的程序设计两者联系起来,使两个数据处理方式能够进行沟通。
2.
游标种类
MS SQL SERVER 支持三种类型的游标:Transact_SQL 游标,API 服务器游标和客户游标。
(1) Transact_SQL 游标
Transact_SQL 游标是由DECLARE CURSOR 语法定义、主要用在Transact_SQL 脚本、存储过程和触发器中。Transact_SQL
游标主要用在服务器上,由从客户端发送给服务器的Transact_SQL 语句或是批处理、存储过程、触发器中的Transact_SQL 进行管理。
Transact_SQL 游标不支持提取数据块或多行数据。
(2) API
游标
API 游标支持在OLE DB,
ODBC 以及DB_library 中使用游标函数,主要用在服务器上。每一次客户端应用程序调用API 游标函数,MS SQL SEVER 的OLE DB
提供者、ODBC驱动器或DB_library
的动态链接库(DLL)都会将这些客户请求传送给服务器以对API游标进行处理。
(3)客户游标
客户游标主要是当在客户机上缓存结果集时才使用。在客户游标中,有一个缺省的结果集被用来在客户机上缓存整个结果集。客户游标仅支持静态游标而非动态游标。由于服务器游标并不支持所有的Transact-SQL
语句或批处理,所以客户游标常常仅被用作服务器游标的辅助。因为在一般情况下,服务器游标能支持绝大多数的游标操作。由于API 游标和Transact-SQL
游标使用在服务器端,所以被称为服务器游标,也被称为后台游标,而客户端游标被称为前台游标。在本章中我们主要讲述服务器(后台)游标。
使用游标
这里要做一个声明,我们所说的游标通常是指显式游标,因此从现在起没有特别指明的情况,我们所说的游标都是指显式游标。要在程序中使用游标,必须首先声明游标。
声明游标
语法:
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
记录变量
定义一个记录变量使用TYPE命令和%ROWTYPE,关于%ROWsTYPE的更多信息请参阅相关资料。
记录变量用于从游标中提取数据行,当游标选择很多列的时候,那么使用记录比为每列声明一个变量要方便得多。
当在表上使用%ROWTYPE并将从游标中取出的值放入记录中时,如果要选择表中所有列,那么在SELECT子句中使用*比将所有列名列出来要安全得多。
例:
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
cursor_name[(parameter[,parameter],...)] IS
select_statement;
定义参数的语法如下:
Parameter_name [IN]
data_type[{:=|DEFAULT}
]
与存储过程不同的是,游标只能接受传递的值,而不能返回值。参数只定义数据类型,没有大小。
另外可以给参数设定一个缺省值,当没有参数值传递给游标时,就使用缺省值。游标中定义的参数只是一个占位符,在别处引用该参数不一定可靠。
在打开游标时给参数赋值,语法如下:
OPEN
cursor_name[[,]....];
参数值可以是文字或变量。
例:
DECALRE
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循环
在大多数时候我们在设计程序的时候都遵循下面的步骤:
1、打开游标
2、开始循环
3、从游标中取值
4、检查那一行被返回
5、处理
6、关闭循环
7、关闭游标
可以简单的把这一类代码称
伪暧糜谘 贰5 褂幸恢盅
酚胝庵掷嘈筒幌嗤饩褪荈OR循环,用于FOR循环的游标按照正常的声明方式声明,它的优点在于不需要显式的打开、关闭、取数据,测试数据的存在、定义存放数据的变量等等。游标FOR
循环的语法如下:
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中的子查询没有什么区别。
游标中的更新和删除
在PL/SQL中依然可以使用UPDATE和DELETE语句更新或删除数据行。显式游标只有在需要获得多行数据的情况下使用。PL/SQL提供了仅仅使用游标就可以执行删除或更新记录的方法。
UPDATE或DELETE语句中的WHERE
CURRENT OF子串专门处理要执行UPDATE或DELETE操作的表中取出的最近的数据。要使用这个方法,在声明游标时必须使用FOR
UPDATE子串,当对话使用FOR
UPDATE子串打开一个游标时,所有返回集中的数据行都将处于行级(ROW-LEVEL)独占式锁定,其他对象只能查询这些数据行,不能进行UPDATE、DELETE或SELECT...FOR
UPDATE操作。
语法:
FOR UPDATE [OF
[schema.]table.column[,[schema.]table.column]..
[nowait]
在多表查询中,使用OF子句来锁定特定的表,如果忽略了OF子句,那么所有表中选择的数据行都将被锁定。如果这些数据行已经被其他会话锁定,那么正常情况下ORACLE将等待,直到数据行解锁。
在UPDATE和DELETE中使用WHERE
CURRENT OF子串的语法如下:
WHERE{CURRENT OF
cursor_name|search_condition}
例:
DELCARE
CURSOR c1 IS
SELECT empno,salary
FROM emp
WHERE comm IS NULL
FOR UPDATE OF
comm;
v_comm NUMBER(10,2);
BEGIN
FOR r1 IN c1
LOOP
IF r1.salary<500 THEN
v_comm:=r1.salary*0.25;
ELSEIF
r1.salary<1000 THEN
v_comm:=r1.salary*0.20;
ELSEIF r1.salary<3000
THEN
v_comm:=r1.salary*0.15;
ELSE
v_comm:=r1.salary*0.12;
END
IF;
UPDATE emp;
SET comm=v_comm
WHERE CURRENT OF c1l;
END
LOOP;
END
我们知道关系数据库管理系统实质是面向集合的,在MS SQL SERVER 中并没有一种描述表中单一记录的表达形式,除非使用where
子句来限制只有一条记录被选中。因此我们必须借助于游标来进行面向单条记录的数据处理。由此可见,游标允许应用程序对查询语句select
返回的行结果集中每一行进行相同或不同的操作,而不是一次对整个结果集进行同一种操作;它还提供对基于游标位置而对表中数据进行删除或更新的能力;而且,正是游标把作为面向集合的数据库管理系统和面向行的程序设计两者联系起来,使两个数据处理方式能够进行沟通。
2.
游标种类
MS SQL SERVER 支持三种类型的游标:Transact_SQL 游标,API 服务器游标和客户游标。
(1) Transact_SQL 游标
Transact_SQL 游标是由DECLARE CURSOR 语法定义、主要用在Transact_SQL 脚本、存储过程和触发器中。Transact_SQL
游标主要用在服务器上,由从客户端发送给服务器的Transact_SQL 语句或是批处理、存储过程、触发器中的Transact_SQL 进行管理。
Transact_SQL 游标不支持提取数据块或多行数据。
(2) API
游标
API 游标支持在OLE DB,
ODBC 以及DB_library 中使用游标函数,主要用在服务器上。每一次客户端应用程序调用API 游标函数,MS SQL SEVER 的OLE DB
提供者、ODBC驱动器或DB_library
的动态链接库(DLL)都会将这些客户请求传送给服务器以对API游标进行处理。
(3)客户游标
客户游标主要是当在客户机上缓存结果集时才使用。在客户游标中,有一个缺省的结果集被用来在客户机上缓存整个结果集。客户游标仅支持静态游标而非动态游标。由于服务器游标并不支持所有的Transact-SQL
语句或批处理,所以客户游标常常仅被用作服务器游标的辅助。因为在一般情况下,服务器游标能支持绝大多数的游标操作。由于API 游标和Transact-SQL
游标使用在服务器端,所以被称为服务器游标,也被称为后台游标,而客户端游标被称为前台游标。在本章中我们主要讲述服务器(后台)游标。
使用游标
这里要做一个声明,我们所说的游标通常是指显式游标,因此从现在起没有特别指明的情况,我们所说的游标都是指显式游标。要在程序中使用游标,必须首先声明游标。
声明游标
语法:
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
记录变量
定义一个记录变量使用TYPE命令和%ROWTYPE,关于%ROWsTYPE的更多信息请参阅相关资料。
记录变量用于从游标中提取数据行,当游标选择很多列的时候,那么使用记录比为每列声明一个变量要方便得多。
当在表上使用%ROWTYPE并将从游标中取出的值放入记录中时,如果要选择表中所有列,那么在SELECT子句中使用*比将所有列名列出来要安全得多。
例:
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
cursor_name[(parameter[,parameter],...)] IS
select_statement;
定义参数的语法如下:
Parameter_name [IN]
data_type[{:=|DEFAULT}
]
与存储过程不同的是,游标只能接受传递的值,而不能返回值。参数只定义数据类型,没有大小。
另外可以给参数设定一个缺省值,当没有参数值传递给游标时,就使用缺省值。游标中定义的参数只是一个占位符,在别处引用该参数不一定可靠。
在打开游标时给参数赋值,语法如下:
OPEN
cursor_name[[,]....];
参数值可以是文字或变量。
例:
DECALRE
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循环
在大多数时候我们在设计程序的时候都遵循下面的步骤:
1、打开游标
2、开始循环
3、从游标中取值
4、检查那一行被返回
5、处理
6、关闭循环
7、关闭游标
可以简单的把这一类代码称
伪暧糜谘 贰5 褂幸恢盅
酚胝庵掷嘈筒幌嗤饩褪荈OR循环,用于FOR循环的游标按照正常的声明方式声明,它的优点在于不需要显式的打开、关闭、取数据,测试数据的存在、定义存放数据的变量等等。游标FOR
循环的语法如下:
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中的子查询没有什么区别。
游标中的更新和删除
在PL/SQL中依然可以使用UPDATE和DELETE语句更新或删除数据行。显式游标只有在需要获得多行数据的情况下使用。PL/SQL提供了仅仅使用游标就可以执行删除或更新记录的方法。
UPDATE或DELETE语句中的WHERE
CURRENT OF子串专门处理要执行UPDATE或DELETE操作的表中取出的最近的数据。要使用这个方法,在声明游标时必须使用FOR
UPDATE子串,当对话使用FOR
UPDATE子串打开一个游标时,所有返回集中的数据行都将处于行级(ROW-LEVEL)独占式锁定,其他对象只能查询这些数据行,不能进行UPDATE、DELETE或SELECT...FOR
UPDATE操作。
语法:
FOR UPDATE [OF
[schema.]table.column[,[schema.]table.column]..
[nowait]
在多表查询中,使用OF子句来锁定特定的表,如果忽略了OF子句,那么所有表中选择的数据行都将被锁定。如果这些数据行已经被其他会话锁定,那么正常情况下ORACLE将等待,直到数据行解锁。
在UPDATE和DELETE中使用WHERE
CURRENT OF子串的语法如下:
WHERE{CURRENT OF
cursor_name|search_condition}
例:
DELCARE
CURSOR c1 IS
SELECT empno,salary
FROM emp
WHERE comm IS NULL
FOR UPDATE OF
comm;
v_comm NUMBER(10,2);
BEGIN
FOR r1 IN c1
LOOP
IF r1.salary<500 THEN
v_comm:=r1.salary*0.25;
ELSEIF
r1.salary<1000 THEN
v_comm:=r1.salary*0.20;
ELSEIF r1.salary<3000
THEN
v_comm:=r1.salary*0.15;
ELSE
v_comm:=r1.salary*0.12;
END
IF;
UPDATE emp;
SET comm=v_comm
WHERE CURRENT OF c1l;
END
LOOP;
END
一般情况下,我们用SELECT这些查询语句时,都是针对的一行记录而言,
如果要在查询分析器中对多行记录(即记录集)进行读取操作时,则需要使用到游标或WHILE等循环
游标的类型:
1、静态游标(不检测数据行的变化)
2、动态游标(反映所有数据行的改变)
3、仅向前游标(不支持滚动)
4、键集游标(能反映修改,但不能准确反映插入、删除)
游标使用顺序:
1、定义游标
2、打开游标
3、使用游标
4、关闭游标
5、释放游标
Transact-SQL:
declare 游标名 cursor [LOCAL | GLOBAL][FORWARD_ONLY | SCROLL][STATIC | KEYSET | DYNAMIC ] [READ_ONLY | SCROLL_LOCKS]
for selet语句 [for update[of 列名[,列名]]
注:LOCAL 局部游标 GLOBAL 全局游标
FORWARD_ONLY 仅向前 SCROLL 滚动
STATIC 静态 KEYSET 键集 DYNAMIC 动态
READ_ONLY 只读 SCROLL_LOCKS 锁定游标当前行
获取游标的数据
FETCH [[NEXT | PRIOR | FIRST | LAST |
ABSOLUTE{ n | @nvar | RELATIVE { n | @nvar}]
From ] 游标名 [into 变量]
注:
NEXT 下一行 PRIOR 上一行 FIRST 第一行
LAST 最后一行 ABSOLUTE n 第n行
RELATIVE n 当前位置开始的第n行
into 变量 把当前行的各字段值赋值给变量
游标状态变量:
@@fetch_status 游标状态
0 成功 -1 失败 -2 丢失
@@cursor_rows 游标中结果集中的行数
n 行数 -1 游标是动态的 0 空集游标
操作游标的当前行:
current of 游标名
use pubs
go
declare @auid char(12),@aulname varchar(20),@aufname varchar(20), @st char(2),@auinfo varchar(50)
declare auth_cur cursor for
select au_id, au_lname, au_fname, state
from authors
open auth_cur
fetch next from auth_cur into @auid,@aulname,@aufname, @st
while (@@fetch_status=0)
begin
print '作者编号: '+@auid
print '作者姓名: '+@aulname+','+@aufname
print '所在州: '+@st
print '--------------------------'
fetch next from auth_cur into @auid,@aulname,@aufname, @st
end
close auth_cur
deallocate auth_cur
declare auth_cur cursor scroll for
select au_id, au_lname, au_fname, state
from authors for update of au_lname
open auth_cur
declare @rowcount int
set @rowcount = 6
fetch absolute @rowcount from auth_cur --将变量@rowcount标识的行设置为当前行
--下面1行是利用游标进行修改操作
update authors set au_lname='张' where current of auth_cur --修改游标中的当前行
--下面1行是利用游标进行删除操作
delete from authors where current of auth_cur
-----------------------------------------------------------------------------------------------------------------------------------------------
作为关系型数据库市场的老大,Oracla占有举足轻重的地位。虽然在操作上不如SQLSERVER那样方便,但是他的强大的功能,还是吸引来大批大批的追随着。本人作为ORACLE菜鸟,在工作当中也偶尔使用Oracle。以下记录的上由于工作需要写的Oracle的
使用游标的储存过程,个人觉得比较有代表性。希望给初学者一定的帮助,也给自己加深一下印象。
在ORACLE中,他以一个语句块为一个默认的事务。也就是说,如果你就单单只执行一段ORACLE的语句块,他默认是以事务的形式执行的。
01 CREATE OR REPLACE PROCEDURE sp_EditInlayOut(
02 FID NUMBER, --修改记录的ID T_INLAYOUT表的主键
03 InlayBoxIDs varchar2, --修改的记录
04 BoxCount number, --装箱数量
05 ApplyUserID varchar2, --申请人编号
06 StoreUserID varchar2, --库管编号
07 ConfirmState char, --确认状态
08 ExistState char, --存在状态
09 strErr OUT varchar2 --存储过程执行结果。成功返回空,失败返回错误原因
10 )
11 AS
12 --定义变量
13 v_Now DATE;
14 v_Now2 date;
15 v_LogID number;
16 v_ChipID number;
17 v_sql varchar2(2000);
18 BEGIN
19
20 --记录日志
21 INSERT INTO T_InlayOut_Log(F_InlayBoxIDs,f_Boxcount,f_Applyuserid,f_Storeuserid,f_Addtime,f_Confirmstate
22 ,f_Existstate, f_modifyid, f_modifytime, f_modifyuserid )
23 ((SELECT F_InlayBoxIDs,f_Boxcount,f_Applyuserid,f_Storeuserid,f_Addtime,f_Confirmstate,f_Existstate
24 ,FID,SYSDATE,StoreUserID FROM T_InlayOut WHERE F_ID=FID));
25 --取刚插入记录的ID
26 select seq_t_inlayout_log.currval into v_LogID from dual;
27 --定义游标
28 DECLARE CURSOR myCusor IS SELECT F_ID FROM T_CHIP WHERE F_InlayBoxID IN (SELECT f_ID FROM
29 T_InlayBox where F_InlayOutID = FID);
30 --开始使用游标取数据
31 BEGIN
32 OPEN myCusor;
33
34 LOOP
35 FETCH myCusor INTO v_ChipID;
36 --游标取不到数据则退出
37 EXIT WHEN myCusor%NOTFOUND;
38
39 SELECT MIN(F_CurrentTime) INTO v_Now FROM t_Chipstatehistory WHERE
40 (F_HistoryState = 'Confirm_InlayIn') AND F_ChipID = v_ChipID;
41 --改变芯片表的状态
42 UPDATEt_chip SET f_State = 'Confirm_InlayIn',F_CompareTime = v_Now WHERE F_ID = v_ChipID;
43 --保存芯片状态历史记录
44 INSERT INTO T_CHIPSTATEHISTORY(f_chipid, f_Historystate,F_TABLEID,f_Currenttime,F_TABLENAME)
45 VALUES
46 (v_ChipID,'Confirm_InlayIn',v_LogID,SYSDATE,'T_InlayOut_Log');
47
48 END LOOP;
49 CLOSE myCusor;
50 END;
51
52 --选择最近芯片状态变更时间
53 --SELECT MIN(F_CURRENTTIME) INTO v_NOW FROM T_CHIPSTATEHISTORY WHERE F_HISTORYSTATE = 20
54 AND F_CHIPID IN (SELECT F_ID FROM T_CHIP WHERE F_InlayBoxID=(SELECT F_ID FROM T_InlayBox
55 WHERE F_InlayOutID=FID));
56
57 --将芯片表中芯片状态更新到以前状态
58 --UPDATE T_CHIP SET F_State=20,F_CompareTime=v_NOW WHERE F_InlayBoxID IN (SELECT F_ID FROM
59 T_InlayBox WHERE F_InlayOutID =FID);
60 --记录芯片状态变更日志
61 --INSERT INTO T_ChipStateHistory (F_ChipID,f_Historystate,f_Tableid,f_Currenttime,f_Tablename)VALUES
62 --((SELECT F_ID FROM T_CHIP WHERE F_InlayBoxID=(SELECT F_ID FROM T_InlayBox WHERE F_InlayOutID=FID)),
63 20,v_LogID,SYSDATE,'T_InlayOut_Log');
64
65
66 --将Inlay出库箱表中以前的数据更新到以前状态
67 UPDATE T_InlayBox SET F_State=2,F_InlayOutID=null WHERE F_InlayOutID =FID;
68
69 --编辑时将新的INLAY出库信息更新
70 UPDATE T_InlayOut SET F_InlayBoxIDs=InlayBoxIDs,f_Boxcount=BoxCount,f_Applyuserid=ApplyUserID,
71 f_Storeuserid=StoreUserID,f_Confirmstate=ConfirmState,F_ExistState=ExistState,F_ConfirmTime=null
72 WHERE F_ID=FID;
73
74 --更新T_InlayBox 新的状态
75 --UPDATE T_InlayBox SET F_State=3,F_InlayOutID=FID WHERE F_ID in (InlayBoxIDs);
76 v_sql := 'UPDATE T_InlayBox SET F_State=3,F_InlayOutID='||FID||' WHERE F_ID in ('||InlayBoxIDs||')';
77 --立即执行v_sql
78 EXECUTE IMMEDIATE v_sql;
79
80 SELECT SYSDATE INTO v_Now2 FROM DUAL;
81 --更新芯片表状态
82 UPDATE T_Chip SET F_State='No_Confirm_InlayOut',F_CompareTime=v_Now2 WHERE F_InlayBoxID IN
83 (SELECT F_ID FROM T_InlayBox WHERE F_InlayOutID=FID);
84 --记录当前操作日志
85 INSERT INTO T_ChipStateHistory (F_ChipID,f_Historystate,f_Tableid,f_Currenttime,f_Tablename)
86 SELECT F_ID,'No_Confirm_InlayOut',v_LogID,v_Now2,'T_InlayOut_Log' FROM T_CHIP WHERE F_InlayBoxID IN
87 (SELECT F_ID FROM T_InlayBox WHERE F_InlayOutID=FID);
88 --提交
89 COMMIT;
90 --发生异常时返回错误码
91 EXCEPTION
92 WHEN OTHERS THEN
93 strErr := substr(sqlerrm,1,100);
94 ROLLBACK;
95 END sp_EditInlayOut;
但是在SQLSERVER中,除非你将所有的T-SQL语句块以显示的方式【BEGIN TRANSACTION ....END TRANSACTION】申明在事务中,否则SQLSERVER会将语句块中的每一句作为一个单独的默认事务执行。
此外,游标是一种比较占I/O资源的操作,使用完后应该及时关闭,以释放系统资源。
---------------------------------------------------------------------------------------------------------------------------------
Oracle游标分为显示游标和隐式游标。
显示游标(Explicit Cursor):在PL/SQL程序中定义的、用于查询的游标称作显示游标。
隐式游标(Implicit Cursor):是指非PL/SQL程序中定义的、而且是在PL/SQL中使用UPDATE/DELETE语句时,Oracle系统自动分配的游标。
一.显示游标
1.使用步骤
(1)定义 (2)打开 (3)使用 (4)关闭
2.使用演示
首先创建测试用表STUDENT,脚本如下:
复制代码 代码如下:
CREATE TABLE "STUDENT" (
"STUNAME" VARCHAR2(10 BYTE),
"STUNO" VARCHAR2(4 BYTE),
"AGE" NUMBER,
"GENDER" VARCHAR2(2 CHAR)
)
(1).使用WHILE循环处理游标
create or replace PROCEDURE PROC_STU1 AS
BEGIN
--显示游标使用,使用while循环
declare
--1.定义游标,名称为cur_stu
cursor cur_stu is
select stuno,stuname from student order by stuno;
--定义变量,存放游标取出的数据
v_stuno varchar(4);
v_stuname varchar(20);
begin
--2.打开游标cur_stu
open cur_stu;
--3.将游标的当前行取出存放到变量中
fetch cur_stu into v_stuno,v_stuname;
while cur_stu%found --游标所指还有数据行,则继续循环
loop
--打印结果
dbms_output.PUT_LINE(v_stuno||'->'||v_stuname);
--继续将游标所指的当前行取出放到变量中
fetch cur_stu into v_stuno,v_stuname;
end loop;
close cur_stu; --4.关闭游标
end;
END PROC_STU1;
(2).使用IF..ELSE代替WHILE循环处理游标
create or replace PROCEDURE PROC_STU2 AS
BEGIN
--显示游标使用,使用if判断
declare
--1.定义游标,名称为cur_stu
cursor cur_stu is
select stuno,stuname from student order by stuno;
--定义变量,存放游标取出的数据
v_stuno varchar(4);
v_stuname varchar(20);
begin
--2.打开游标cur_stu
open cur_stu;
--3.将游标的当前行取出存放到变量中
fetch cur_stu into v_stuno,v_stuname;
loop
if cur_stu%found then --如果游标cur_stu所指还有数据行
--打印结果
dbms_output.PUT_LINE(v_stuno||'->'||v_stuname);
--继续将游标所指的当前行取出放到变量中
fetch cur_stu into v_stuno,v_stuname;
else
exit;
end if;
end loop;
close cur_stu; --4.关闭游标
end;
END PROC_STU2;
(3).使用FOR循环处理游标
create or replace PROCEDURE PROC_STU3 AS
BEGIN
--显示游标使用,使用for循环
declare
--定义游标,名称为cur_stu
cursor cur_stu is
select stuno,stuname from student order by stuno;
begin
for stu in cur_stu
loop
dbms_output.PUT_LINE(stu.stuno||'->'||stu.stuname);
--循环做隐含检查 %notfound
end loop;
--自动关闭游标
end;
END PROC_STU3;
(4).常用的使用EXIT WHEN处理游标
create or replace
PROCEDURE PROC_STU1_1 AS
BEGIN
--显示游标使用,使用exit when循环
declare
--1.定义游标,名称为cur_stu
cursor cur_stu is
select stuno,stuname from student order by stuno;
--定义变量,存放游标取出的数据
v_stuno varchar(4);
v_stuname varchar(20);
begin
--2.打开游标cur_stu
open cur_stu;
loop
--3.将游标的当前行取出存放到变量中
fetch cur_stu into v_stuno,v_stuname;
exit when cur_stu%notfound; --游标所指还有数据行,则继续循环
--打印结果
dbms_output.PUT_LINE(v_stuno||'->'||v_stuname);
end loop;
close cur_stu; --4.关闭游标
end;
END PROC_STU1_1;
二.隐式游标
1.使用演示
create or replace PROCEDURE PROC_STU4 AS
BEGIN
--隐式游标使用
update student set stuname='张燕广' where stuno='1104';
--如果更新没有匹配则插入一条新记录
if SQL%NOTFOUND then
insert into student(STUNO,STUNAME,AGE,GENDER)
values('1104','张燕广',18,'男');
end if;
END PROC_STU4;
2.说明
所有的SQL语句在上下文区内部都是可执行的,因为都有一个游标指向上下文区,此游标就是
SQL游标,与现实游标不同的是,SQL游标在PL/SQL中不需要打开和关闭,而是在执行UPDATE、
DELETE是自动打开和关闭。
上面例子中就是通过SQL%NOTFOUND游标属性判断UPDATE语句的执行结果决定是否需要插入新记录。
详细出处参考:http://www.jb51.net/article/20237.htm
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/26613085/viewspace-1126615/ ,如需转载,请注明出处,否则将追究法律责任。