sqlserver 进阶(四)游标的使用

游标是从数据表中提取出来的数据,以临时表的形式存放在内存中,
在游标中有一个数据指针,在初始状态下指向的是首记录,
利用 FETCH 语句可以移动该指针,从而对游标中的数据进行各种操作,
然后将操作结果写回到数据表中。
游标分为显式游标和隐式游标两种。
显式游标主要是用于对查询语句的处理,尤其是在查询结果为多条记录的情况下;而对于非查询语句,如修改、删除操作,则由系统自动地为这些操作设置游标并创建其工作区,这些由系统隐含创建的游标称为隐式游标,隐式游标的名字为 SQL,这是由系统定义的。对于隐式游标的操作,如定义、打开、取值及关闭操作,都由系统自动地完成,无需用户进行处理。用户只能通过隐式游标的相关属性,来完成相应的操作。在隐式游标的工作区中,所存放的数据是与用户自定义的显示游标无关的、最新处理的一条 SQL 语句所包含的数据。INSERT, UPDATE, DELETE, SELECT INTO 语句中不必明确定义游标
显式游标是由用户声明和操作的一种游标。 以下主要针对显式游标做处理。

1、游标的声明

声明游标的语句格式如下:

declare cursor_name CURSOR  for select_statement;

声明游标完成了下面两个目的:
(1)给游标命名;
(2)将一个查询与游标关联起来。
varchar 字段需要指定长度,不指定长度默认长度为 0

2、打开游标

打开游标的语句格式如下:

OPEN cursor_name;

打开游标将激活查询并识别活动集,可是在执行游标取回命令之前,并没有真正取回记录。
OPEN 命令还初始化了游标指针,使其指向活动集的第一条记录。游标被打开后,直到关闭之前,取回到活动集的所有数据都是静态的。换句话说,游标忽略所有在游标打开之后,对数据执行的 SQL DML 命令 (INSERT、UPDATE、DELETE 和 SELECT),因此只有在需要时才打开它,要刷新活动集,只需关闭并重新打开游标即可。

3、从游标中取回数据

FETCH 命令以每次一条记录的方式取回活动集中的记录。通常将 FETCH 命令和某种迭代处理结合起来使用,在迭代处理中,FETCH 命令每执行一次,游标前进到活动集的下一条记录。
FETCH 命令的语句格式如下:

FETCH next from cursor_name INTO record_list;

执行 FETCH 命令后,活动集中的结果被取回到 PL/SQL 变量中,以便在 PL/SQL 块中使用。每取回一条记录,游标的指针就移向活动集的下一条记录。 当检索到最后一行数据时,如果再次执行 FETCH 语句,操作将失败

@@fetch_status=0

4、关闭游标

CLOSE 语句关闭以前打开的游标,以释放它所占用的系统资源。
其语法格式为:

 CLOSE cursor_name;
摧毁游标
--摧毁游标
deallocate cursor_name

示例

--摧毁游标
deallocate emp_cur

5、嵌套游标示例

例 1:
declare @empno int
declare @mgr int
declare @job varchar(50)
--定义一个游标
declare emp_cur cursor for select EMPNO,MGR,JOB from emp
--打开游标
open emp_cur
while @@fetch_status=0
begin
--读取游标
fetch next from emp_cur into @empno,@mgr,@job
print @empno
print @mgr
print @job
end
close emp_cur
例 2:
declare @deptno int
declare @dname varchar(14)
declare @loc varchar(50)

--定义一个游标
declare deptList cursor for select deptno,dname,loc from dept
--打开游标
open deptList
while @@fetch_status=0
begin
--读取游标
fetch next from deptList into @deptno,@dname,@loc
print @deptno
print @dname
end
close deptList
--摧毁游标
deallocate deptList
例 3:
--嵌套游标
--声明游标
declare v_dept_cursor cursor  for select DEPTNO,DNAME,LOC from dept
declare @v_deptno int
declare @v_dname varchar(14)
declare @v_loc varchar(13)
declare @tot_sal int
declare @v_ename varchar(14)
declare @v_sal decimal
declare @v_tot_sal decimal

--打开游标
open v_dept_cursor
--while循环之外外层游标向下走第一步
fetch next from  v_dept_cursor into @v_deptno,@v_dname,@v_loc
while (@@fetch_status=0)
begin
--读取数据
print @v_loc
print '部门编号'+cast(@v_deptno as varchar)+'部门名称:'+@v_dname
set @v_tot_sal=0
--打开游标
declare v_emp_cursor cursor  for select ename,sal from emp where deptno=@v_deptno
open v_emp_cursor
--while循环之外内层游标向下走第一步
fetch next from  v_emp_cursor into @v_ename,@v_sal
while (@@fetch_status=0)
begin
print '员工姓名:'+@v_ename+'员工薪资:'+cast(@v_sal as varchar)
set @v_tot_sal=@v_tot_sal+@v_sal
print '部门薪资总和:'+cast(@v_tot_sal	as varchar)
--内层游标循环向下取数据
fetch next from  v_emp_cursor into @v_ename,@v_sal
end
close v_emp_cursor
--内存游标循环创建,摧毁之前重名的游标
deallocate v_emp_cursor
--外层游标循环向下取数据,在内循环执行完毕后再fetch外循环数据,更新fetch_status的值,因为此时内循环将fetch_status的值设置为了-1或者-2,此时外循环还有数据,通过fetch next可以将fetch_status的值改变成我们外循环的实际状态
fetch next from  v_dept_cursor into @v_deptno,@v_dname,@v_loc
end
close v_dept_cursor