ITPub博客

首页 > 数据库 > Oracle > 游标的作用(R1)

游标的作用(R1)

Oracle 作者:thamsyangsw 时间:2014-03-21 14:29:38 0 删除 编辑
http://blog.csdn.net/chenjie19891104/article/details/4424886
游标的概念和作用
游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制。游标充当指针的作用。尽管游标能遍历结果中的所有行,但他一次只指向一行。

概括来讲,SQL的游标是一种临时的数据库对象,即可以用来存放在数据库表中的数据行副本,也可以指向存储在数据库中的数据行的指针。游标提供了在逐行的基础上操作表中数据的方法。

游标的一个常见用途就是保存查询结果,以便以后使用。游标的结果集是由SELECT语句产生,如果处理过程需要重复使用一个记录集,那么创建一次游标而重复使用若干次,比重复查询数据库要快的多。

大部分程序数据设计语言都能使用游标来检索SQL数据库中的数据,在程序中嵌入游标和在程序中嵌入SQL语句相同
----------------------------------------------------------------------------------------------------------------------------------------------
游标是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果

每个游标区都有一个名字

用户可以用SQL语句逐一从游标中获取记录,并赋给主变量,交由主语言进一步处理

主语言是面向记录的,一组主变量一次只能存放一条记录

仅使用主变量并不能完全满足SQL语句向应用程序输出数据的要求

嵌入式SQL引入了游标的概念,用来协调这两种不同的处理方式
游标是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果

每个游标区都有一个名字

用户可以用SQL语句逐一从游标中获取记录,并赋给主变量,交由主语言进一步处理

主语言是面向记录的,一组主变量一次只能存放一条记录

仅使用主变量并不能完全满足SQL语句向应用程序输出数据的要求

嵌入式SQL引入了游标的概念,用来协调这两种不同的处理方式
----------------------------------------------------------------------------------------------------------------------------------------------
数据库游标的概念和作用
数据库游标的概念和作用

游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制。游标充当指针的作用。尽管游标能遍历结果中的所有行,但他一次只指向一行。

概括来讲,SQL的游标是一种临时的数据库对象,即可以用来存放在数据库表中的数据行副本,也可以指向存储在数据库中的数据行的指针。游标提供了在逐行的基础上操作表中数据的方法。

游标的一个常见用途就是保存查询结果,以便以后使用。游标的结果集是由SELECT语句产生,如果处理过程需要重复使用一个记录集,那么创建一次游标而重复使用若干次,比重复查询数据库要快的多。

大部分程序数据设计语言都能使用游标来检索SQL数据库中的数据,在程序中嵌入游标和在程序中嵌入SQL语句相同



1游标和游标的优点
                
   
在数据库中,游标是一个十分重要的概念。游标提供了一种对从表中检索出的数据进行操作的灵活手段,就本质而言,游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制。游标总是与一条SQL
选择语句相关联因为游标由结果集(可以是零条、一条或由相关的选择语句检索出的多条记录)和结果集中指向特定记录的游标位置组成。当决定对结果集进行处理时,必须声明一个指向该结果集的游标。如果曾经用
C
语言写过对文件进行处理的程序,那么游标就像您打开文件所得到的文件句柄一样,只要文件打开成功,该文件句柄就可代表该文件。对于游标而言,其道理是相同的。可见游标能够实现按与传统程序读取平面文件类似的方式处理来自基础表的结果集,从而把表中数据以平面文件的形式呈现给程序。


   
我们知道关系数据库管理系统实质是面向集合的,在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 

i.       声明游标 
在这一步中,需要指定游标的属性和根据要求产生的结果集。有两种方法可以指定一个游标。 
形式1 (ANSI
92) 

DECLARE
cursor_name [INSENSITIVE] [SCROLL] CURSOR 

FOR
select_statement 

[FOR
{READ ONLY | UPDATE ][OF column_list]}] 

形式2 
DECLARE
cursor_name CURSOR 

[LOCAL
| GLOBAL] 

[FORWARD_ONLY
| SCROLL] 

[STATIC
| KEYSET | DYNAMIC] 

[READ_ONLY
| SCROLL_LOCKS | OPTIMISTIC] 

FOR
select_statement 

[FOR
{READ ONLY | UPDATE ][OF column_list]}] 

INSENSITIVE关键字指明要为检索到的结果集建立一个临时拷贝,以后的数据从这个临时拷贝中获取。如果在后来游标处理的过程中,原有基表中数据发生了改变,那么它们对于该游标而言是不可见的。这种不敏感的游标不允许数据更改。 
SCROLL关键字指明游标可以在任意方向上滚动。所有的fetch选项(first、last、next、relative、absolute)都可以在游标中使用。如果忽略该选项,则游标只能向前滚动(next)。 
Select_statement指明SQL语句建立的结果集。Transact
SQL语句COMPUTE、COMPUTE BY、FOR BROWSE和INTO在游标声明的选择语句中不允许使用。 

READ
ONLY指明在游标结果集中不允许进行数据修改。 

UPDATE关键字指明游标的结果集可以修改。 
OF
column_list指明结果集中可以进行修改的列。缺省情况下(使用UPDATE关键字),所有的列都可进行修改。 

LOCAL关键字指明游标是局部的,它只能在它所声明的过程中使用。 
GLOBAL关键字使得游标对于整个连接全局可见。全局的游标在连接激活的任何时候都是可用的。只有当连接结束时,游标才不再可用。 
FORWARD_ONLY指明游标只能向前滚动。 
STATIC的游标与INSENSITIVE的游标是相同的。 
KEYSET指明选取的行的顺序。SQL
Server将从结果集中创建一个临时关键字集。如果对数据库的非关键字列进行了修改,则它们对游标是可见的。因为是固定的关键字集合,所以对关键字列进行修改或新插入列是不可见的。 

DYNAMIC指明游标将反映所有对结果集的修改。 
SCROLL_LOCK是为了保证游标操作的成功,而对修改或删除加锁。 
OPTIMISTIC指明哪些通过游标进行的修改或者删除将不会成功。 
注意: 
·
如果在SELECT语句中使用了DISTINCT、UNION、GROUP
BY语句,且在选择中包含了聚合表达式,则游标自动为INSENSITIVE的游标。 

·
如果基表没有唯一的索引,则游标创建成INSENSITIVE的游标。 

·
如果SELECT语句包含了ORDER BY,而被ORDER
BY的列并非唯一的行标识,则DYNAMIC游标将转换成KEYSET游标。如果KEYSET游标不能打开,则将转换成INSENSITIVE游标。使用SQL
ANSI-92语法定义的游标同样如此,只是没有INSENSITIVE关键字而已。 

           
ii.       打开游标 

打开游标就是创建结果集。游标通过DECLARE语句定义,但其实际的执行是通过OPEN语句。语法如下: 
OPEN
{ { [GLOBAL] cursor_name } | cursor_variable_name} 

GLOBAL指明一个全局游标。 
Cursor_name是被打开的游标的名称。 
Cursor_variable_name是所引用游标的变量名。该变量应该为游标类型。 
在游标被打开之后,系统变量@@cursor_rows可以用来检测结果集的行数。@@cursor_rows为负数时,表示游标正在被异步迁移,其绝对值(如果@@cursor_rows为-5,则绝对值为5)为当前结果集的行数。异步游标使用户在游标被完全迁移时仍然能够访问游标的结果。 
           iii.       从游标中取值 
在从游标中取值的过程中,可以在结果集中的每一行上来回移动和处理。如果游标定义成了可滚动的(在声明时使用SCROLL关键字),则任何时候都可取出结果集中的任意行。对于非滚动的游标,只能对当前行的下一行实施取操作。结果集可以取到局部变量中。Fetch命令的语法如下: 
FETCH
[NEXT | PRIOR| FIRST | LAST | ABSOLUTE {n | @nvar} | RELATIVE {n |
@nvar}] 

FROM
[GLOBAL] cursor_name} | cursor_variable_name} 

[INTO
@variable_name ][,……n]] 

NEXT指明从当前行的下一行取值。 
PRIOR指明从当前行的前一行取值。 
FIRST是结果集的第一行。 
LAST是结果集的最后一行。 
ABSOLUTE
n表示结果集中的第n行,该行数同样可以通过一个局部变量传播。行号从0开始,所以n为0时不能得到任何行。 

RELATIVE
n表示要取出的行在当前行的前n行或后n行的位置上。如果该值为正数,则要取出的行在当前行前n行的位置上,如果该值为负数,则返回当前行的后n行。 

INTO
@cursor_variable_name表示游标列值存储的地方的变量列表。该列表中的变量数应该与DECLARE语句中选择语句所使用的变量数相同。变量的数据类型也应该与被选择列的数据类型相同。直到下一次使用FETCH语句之前,变量中的值都会一直保持。 

每一次FETCH的执行都存储在系统变量@@fetch_status中。如果FETCH成功,则@@fetch_status被设置成0。@@fetch_status为-1表示已经到达了结果集的一部分(例如,在游标被打开之后,基表中的行被删除)。@@fetch_status可以用来构造游标处理的循环。 
例如: 
DECLARE
@iname char(20), @fname char(20) 

OPEN
author_cur 

FETCH
FIRST FROM author_cur INTO @iname, @fname 

WHILE
@@fetch_status = 0 

BEGIN 
IF
@fname = ‘Albert’ 

PRINT
“Found Albert Ringer” 

ELSE 
Print
“Other Ringer” 

FETCH
NEXT FROM author_cur INTO @iname, @fname 

END 
          
iv.       关闭游标 

CLOSE语句用来关闭游标并释放结果集。游标关闭之后,不能再执行FETCH操作。如果还需要使用FETCH语句,则要重新打开游标。语法如下: 
CLOSE
[GLOBAL] cursor_name | cursor_variable_name 

           
v.       释放游标 

游标使用不再需要之后,要释放游标。DEALLOCATE语句释放数据结构和游标所加的锁。语法如下: 
DEALLOCATE
[GLOBAL] cursor_name | cursor_variable_name 


   
下面给出游标的一个完整的例子: 

   
USE master 

   
GO 

   
CREATE PROCEDURE sp_BuildIndexes 

   
AS 

   
DECLARE @TableName sysname, @msg varchar(100), @cmd varchar(100) 


   
DECLARE table_cur CURSOR FOR 

   
SELECT name FROM sysobjects WHERE type=’u’ 


   
OPEN table_cur 

   
FETCH NEXT FROM table_cur INTO @TableName 

    
   
WHILE @@fetch_status = 0 

   
BEGIN 

       IF
@@fetch_status = -2 

          
CONTINUE 

       SELECT
@msg = “Building indexes for table”+@TableName+”…” 

       PRINT
@msg 

       SELECT
@cmd = “DBCC DBREINDEX (‘”+@TableName+”')” 

       EXEC
(@cmd) 

       PRINT
“  “ 

       FETCH
NEXT FROM table_cur INTO @TableName 

   
END 

   
DEALLOCATE table_cur 

   
GO 

   下面的脚本将为PUBS数据库执行sp_BuildIndexes 
   
USE pubs 

   
GO 

   
EXEC ap_BuildIndexes 

   
注意:上面也是创建用户定义的系统存储过程的示例。 


使用临时表 
   临时表是在TempDB中创建的表。临时表的名称都以“#”开头。临时表的范围为创建临时表的连接。因为,临时表不能在两个连接之间共享,一旦连接关闭,临时表就会被丢弃。如果临时表被创建于存储过程之中,则临时表的范围在存储过程之中,或者被该存储过程调用的任何存储过程之中。如果需要在连接之间共享临时表,则需要使用全局的临时表。全局的临时表以“##”符号开头,它将一直存在于数据库中,直到SQL
Server重新启动。一旦这类临时表创建之后,所有的用户都可以访问到。在临时表上不能明确地指明权限。   临时表提供了存储中间结果的能力。有时候,临时表还能通过将一个复杂的查询分解成两个查询而获得性能的改善。这可以通过首先将第一个查询的结果存在临时表中,然后在第二个查询中使用临时表来实现。当一个大表中的某个子集在一个在座过程中使用多次时,建议使用临时表。在这种情况下,在临时表中保持数据的子集,以在随后的连接中使用,这样能大大改善性能。还可以在临时表中创建索引。

----------------------------------------------------------------------------------------------------------------------------------------------


游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制。游标充当指针的作用。尽管游标能遍历结果中的所有行,但他一次只指向一行。

概括来讲,SQL的游标是一种临时的数据库对象,即可以用来存放在数据库表中的数据行副本,也可以指向存储在数据库中的数据行的指针。游标提供了在逐行的基础上操作表中数据的方法。

游标的一个常见用途就是保存查询结果,以便以后使用。游标的结果集是由SELECT语句产生,如果处理过程需要重复使用一个记录集,那么创建一次游标而重复使用若干次,比重复查询数据库要快的多。

大部分程序数据设计语言都能使用游标来检索SQL数据库中的数据,在程序中嵌入游标和在程序中嵌入SQL语句相同



1游标和游标的优点
                
   
在数据库中,游标是一个十分重要的概念。游标提供了一种对从表中检索出的数据进行操作的灵活手段,就本质而言,游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制。游标总是与一条SQL
选择语句相关联因为游标由结果集(可以是零条、一条或由相关的选择语句检索出的多条记录)和结果集中指向特定记录的游标位置组成。当决定对结果集进行处理时,必须声明一个指向该结果集的游标。如果曾经用
C
语言写过对文件进行处理的程序,那么游标就像您打开文件所得到的文件句柄一样,只要文件打开成功,该文件句柄就可代表该文件。对于游标而言,其道理是相同的。可见游标能够实现按与传统程序读取平面文件类似的方式处理来自基础表的结果集,从而把表中数据以平面文件的形式呈现给程序。


   
我们知道关系数据库管理系统实质是面向集合的,在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 

i.       声明游标 
在这一步中,需要指定游标的属性和根据要求产生的结果集。有两种方法可以指定一个游标。 
形式1 (ANSI
92) 

DECLARE
cursor_name [INSENSITIVE] [SCROLL] CURSOR 

FOR
select_statement 

[FOR
{READ ONLY | UPDATE ][OF column_list]}] 

形式2 
DECLARE
cursor_name CURSOR 

[LOCAL
| GLOBAL] 

[FORWARD_ONLY
| SCROLL] 

[STATIC
| KEYSET | DYNAMIC] 

[READ_ONLY
| SCROLL_LOCKS | OPTIMISTIC] 

FOR
select_statement 

[FOR
{READ ONLY | UPDATE ][OF column_list]}] 

INSENSITIVE关键字指明要为检索到的结果集建立一个临时拷贝,以后的数据从这个临时拷贝中获取。如果在后来游标处理的过程中,原有基表中数据发生了改变,那么它们对于该游标而言是不可见的。这种不敏感的游标不允许数据更改。 
SCROLL关键字指明游标可以在任意方向上滚动。所有的fetch选项(first、last、next、relative、absolute)都可以在游标中使用。如果忽略该选项,则游标只能向前滚动(next)。 
Select_statement指明SQL语句建立的结果集。Transact
SQL语句COMPUTE、COMPUTE BY、FOR BROWSE和INTO在游标声明的选择语句中不允许使用。 

READ
ONLY指明在游标结果集中不允许进行数据修改。 

UPDATE关键字指明游标的结果集可以修改。 
OF
column_list指明结果集中可以进行修改的列。缺省情况下(使用UPDATE关键字),所有的列都可进行修改。 

LOCAL关键字指明游标是局部的,它只能在它所声明的过程中使用。 
GLOBAL关键字使得游标对于整个连接全局可见。全局的游标在连接激活的任何时候都是可用的。只有当连接结束时,游标才不再可用。 
FORWARD_ONLY指明游标只能向前滚动。 
STATIC的游标与INSENSITIVE的游标是相同的。 
KEYSET指明选取的行的顺序。SQL
Server将从结果集中创建一个临时关键字集。如果对数据库的非关键字列进行了修改,则它们对游标是可见的。因为是固定的关键字集合,所以对关键字列进行修改或新插入列是不可见的。 

DYNAMIC指明游标将反映所有对结果集的修改。 
SCROLL_LOCK是为了保证游标操作的成功,而对修改或删除加锁。 
OPTIMISTIC指明哪些通过游标进行的修改或者删除将不会成功。 
注意: 
·
如果在SELECT语句中使用了DISTINCT、UNION、GROUP
BY语句,且在选择中包含了聚合表达式,则游标自动为INSENSITIVE的游标。 

·
如果基表没有唯一的索引,则游标创建成INSENSITIVE的游标。 

·
如果SELECT语句包含了ORDER BY,而被ORDER
BY的列并非唯一的行标识,则DYNAMIC游标将转换成KEYSET游标。如果KEYSET游标不能打开,则将转换成INSENSITIVE游标。使用SQL
ANSI-92语法定义的游标同样如此,只是没有INSENSITIVE关键字而已。 

           
ii.       打开游标 

打开游标就是创建结果集。游标通过DECLARE语句定义,但其实际的执行是通过OPEN语句。语法如下: 
OPEN
{ { [GLOBAL] cursor_name } | cursor_variable_name} 

GLOBAL指明一个全局游标。 
Cursor_name是被打开的游标的名称。 
Cursor_variable_name是所引用游标的变量名。该变量应该为游标类型。 
在游标被打开之后,系统变量@@cursor_rows可以用来检测结果集的行数。@@cursor_rows为负数时,表示游标正在被异步迁移,其绝对值(如果@@cursor_rows为-5,则绝对值为5)为当前结果集的行数。异步游标使用户在游标被完全迁移时仍然能够访问游标的结果。 
           iii.       从游标中取值 
在从游标中取值的过程中,可以在结果集中的每一行上来回移动和处理。如果游标定义成了可滚动的(在声明时使用SCROLL关键字),则任何时候都可取出结果集中的任意行。对于非滚动的游标,只能对当前行的下一行实施取操作。结果集可以取到局部变量中。Fetch命令的语法如下: 
FETCH
[NEXT | PRIOR| FIRST | LAST | ABSOLUTE {n | @nvar} | RELATIVE {n |
@nvar}] 

FROM
[GLOBAL] cursor_name} | cursor_variable_name} 

[INTO
@variable_name ][,……n]] 

NEXT指明从当前行的下一行取值。 
PRIOR指明从当前行的前一行取值。 
FIRST是结果集的第一行。 
LAST是结果集的最后一行。 
ABSOLUTE
n表示结果集中的第n行,该行数同样可以通过一个局部变量传播。行号从0开始,所以n为0时不能得到任何行。 

RELATIVE
n表示要取出的行在当前行的前n行或后n行的位置上。如果该值为正数,则要取出的行在当前行前n行的位置上,如果该值为负数,则返回当前行的后n行。 

INTO
@cursor_variable_name表示游标列值存储的地方的变量列表。该列表中的变量数应该与DECLARE语句中选择语句所使用的变量数相同。变量的数据类型也应该与被选择列的数据类型相同。直到下一次使用FETCH语句之前,变量中的值都会一直保持。 

每一次FETCH的执行都存储在系统变量@@fetch_status中。如果FETCH成功,则@@fetch_status被设置成0。@@fetch_status为-1表示已经到达了结果集的一部分(例如,在游标被打开之后,基表中的行被删除)。@@fetch_status可以用来构造游标处理的循环。 
例如: 
DECLARE
@iname char(20), @fname char(20) 

OPEN
author_cur 

FETCH
FIRST FROM author_cur INTO @iname, @fname 

WHILE
@@fetch_status = 0 

BEGIN 
IF
@fname = ‘Albert’ 

PRINT
“Found Albert Ringer” 

ELSE 
Print
“Other Ringer” 

FETCH
NEXT FROM author_cur INTO @iname, @fname 

END 
          
iv.       关闭游标 

CLOSE语句用来关闭游标并释放结果集。游标关闭之后,不能再执行FETCH操作。如果还需要使用FETCH语句,则要重新打开游标。语法如下: 
CLOSE
[GLOBAL] cursor_name | cursor_variable_name 

           
v.       释放游标 

游标使用不再需要之后,要释放游标。DEALLOCATE语句释放数据结构和游标所加的锁。语法如下: 
DEALLOCATE
[GLOBAL] cursor_name | cursor_variable_name 


   
下面给出游标的一个完整的例子: 

   
USE master 

   
GO 

   
CREATE PROCEDURE sp_BuildIndexes 

   
AS 

   
DECLARE @TableName sysname, @msg varchar(100), @cmd varchar(100) 


   
DECLARE table_cur CURSOR FOR 

   
SELECT name FROM sysobjects WHERE type=’u’ 


   
OPEN table_cur 

   
FETCH NEXT FROM table_cur INTO @TableName 

    
   
WHILE @@fetch_status = 0 

   
BEGIN 

       IF
@@fetch_status = -2 

          
CONTINUE 

       SELECT
@msg = “Building indexes for table”+@TableName+”…” 

       PRINT
@msg 

       SELECT
@cmd = “DBCC DBREINDEX (‘”+@TableName+”')” 

       EXEC
(@cmd) 

       PRINT
“  “ 

       FETCH
NEXT FROM table_cur INTO @TableName 

   
END 

   
DEALLOCATE table_cur 

   
GO 

   下面的脚本将为PUBS数据库执行sp_BuildIndexes 
   
USE pubs 

   
GO 

   
EXEC ap_BuildIndexes 

   
注意:上面也是创建用户定义的系统存储过程的示例。 


使用临时表 
   临时表是在TempDB中创建的表。临时表的名称都以“#”开头。临时表的范围为创建临时表的连接。因为,临时表不能在两个连接之间共享,一旦连接关闭,临时表就会被丢弃。如果临时表被创建于存储过程之中,则临时表的范围在存储过程之中,或者被该存储过程调用的任何存储过程之中。如果需要在连接之间共享临时表,则需要使用全局的临时表。全局的临时表以“##”符号开头,它将一直存在于数据库中,直到SQL
Server重新启动。一旦这类临时表创建之后,所有的用户都可以访问到。在临时表上不能明确地指明权限。   临时表提供了存储中间结果的能力。有时候,临时表还能通过将一个复杂的查询分解成两个查询而获得性能的改善。这可以通过首先将第一个查询的结果存在临时表中,然后在第二个查询中使用临时表来实现。当一个大表中的某个子集在一个在座过程中使用多次时,建议使用临时表。在这种情况下,在临时表中保持数据的子集,以在随后的连接中使用,这样能大大改善性能。还可以在临时表中创建索引。
----------------------------------------------------------------------------------------------------------------------------------------------


---------------------------------------------------------------------------------------------------------------------------------------------
http://www.cnblogs.com/tianguook/archive/2011/03/09/1977987.html

 

 

 

一般情况下,我们用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
游标名

例1:利用游标对查询的数据集进行遍历

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

例2:利用游标修改、删除数据

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/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2012-01-12

  • 博文量
    161
  • 访问量
    1235410