首页 > 代码库 > Sql Server游标的使用

Sql Server游标的使用

Sql Server游标的使用

一、TSQL和SQL的区别

SQL是Structrued Query Language的缩写,即结构化查询语言。1987年,“国际标准化组织(ISO)”把ANSI SQL作为国际标准。这个标准在1992年进行了修订(SQL-92),1999年再次修订(SQL-99)。(目前最新的是SQL-2003)(不知道现在情况)

各个厂商在自己的产品中对ANSI的SQL进行扩展,微软使用的SQL标准称为Transact SQL,简称TSQL;而Oraclec的则称为PL/SQL。

 

T-SQL只能在SQL SERVER上使用。它是ANSI SQL的加强版语言、提供了标准的SQL命令。另外,T-SQL还对SQL做了许多补允,提供了数据库脚本语言,即类似C、Basic和Pascal的基本功能,如变量说明、流控制语言、功能函数等。

 

PL-SQL(Procedural Language-SQL)是一种增加了过程化概念的SQL语言,是Oracle对SQL的扩充。与标准SQL语言相同,PL-SQL也是Oracle客户端工具(如SQL*Plus、Developer/2000等)访问服务器的操作语言。它有标准SQL所没有的特征:变量(包括预先定义的和自定义的);控制结构(如IF-THEN-ELSE等流控制语句);自定义的存储过程和函数 ;对象类型等。由于 P/L-SQL 融合了SQL语言的灵活性和过程化的概念,使得P/L-SQL成为了一种功能强大的结构化语言,可以设计复杂的应用。

二、什么是游标

游标其实可以理解成一个定义在特定数据集上的指针,我们可以控制这个指针遍历数据集,或者仅仅是指向特定的行,所以游标是定义在以Select开始的数据集上的:

三、游标的特点

1.在关系型数据库中,对于查询的思考是面向集合的。而游标打破了这一规则,游标使得我们思考方式变为逐行进行。

2.上面的特点导致了在性能上,游标会吃更多的内存,减少可用的并发,占用宽带,锁定资源,当然还有更多的代码量。

从游标对数据库的读取方式来说,不难看出游标为什么占用更多的资源,就好比从ATM取钱,一次取1000效率肯定比取10次100的效率高。

四、为什么学习游标

我个人认为存在既是合理.

1.现存系统有一些游标,我们查询必须通过游标来实现。

2.作为一个备用方式,当我们穷尽了while循环,子查询,临时表,表变量,自建函数或其他方式扔来无法实现某些查询的时候,使用游标实现。

五、T-SQL中游标的生命周期以及实现

周期一:定义游标

T-SQL中的游标定义在MSDN中如下:

1 DECLARE cursor_name CURSOR [ LOCAL | GLOBAL ] 
2      [ FORWARD_ONLY | SCROLL ] 
3      [ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ] 
4      [ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ] 
5      [ TYPE_WARNING ] 
6      FOR select_statement 
7      [ FOR UPDATE [ OF column_name [ ,...n ] ] ]
8 [;]

参数说明:

 LOCAL和GLOBAL二选一

  LOCAL意味着游标的生存周期只在批处理或函数或存储过程中可见,而GLOBAL意味着游标对于特定连接作为上下文,全局内有效,如果不指定游标作用域,默认作用域为GLOBAL

 FORWARD_ONLY 和 SCROLL 二选一

  FORWARD_ONLY意味着游标只能从数据集开始向数据集结束的方向读取,FETCH NEXT是唯一的选项,而SCROLL支持游标在定义的数据集中向任何方向,或任何位置移动,支持6种移动选项,分别为到第一行(FIRST),最后一行(LAST),下一行(NEXT),上一行(PRIOR),直接跳到某行(ABSOLUTE(n)),相对于目前跳几行(RELATIVE(n))

STATIC  KEYSET  DYNAMIC  和 FAST_FORWARD 四选一

这四个关键字是游标所在数据集所反应的表内数据和游标读取出的数据的关系

  STATIC意味着,当游标被建立时,将会创建FOR后面的SELECT语句所包含数据集的副本存入tempdb数据库中,任何对于底层表内数据的更改不会影响到游标的内容.

       DYNAMIC是和STATIC完全相反的选项,当底层数据库更改时,游标的内容也随之得到反映,在下一次fetch中,数据内容会随之改变

       KEYSET可以理解为介于STATIC和DYNAMIC的折中方案。将游标所在结果集的唯一能确定每一行的主键存入tempdb,当结果集中任何行改变或者删除时,@@FETCH_STATUS会为-2,KEYSET无法探测新加入的数据

      FAST_FORWARD可以理解成FORWARD_ONLY的优化版本.FORWARD_ONLY执行的是静态计划,而FAST_FORWARD是根据情况进行选择采用动态计划还是静态计划,大多数情况下FAST_FORWARD要比FORWARD_ONLY性能略好.

READ_ONLY  SCROLL_LOCKS  OPTIMISTIC 三选一 

 

    READ_ONLY意味着声明的游标只能读取数据,游标不能做任何更新操作

 

      SCROLL_LOCKS是另一种极端,将读入游标的所有数据进行锁定,防止其他程序进行更改,以确保更新的绝对成功

 

      OPTIMISTIC是相对比较好的一个选择,OPTIMISTIC不锁定任何数据,当需要在游标中更新数据时,如果底层表数据更新,则游标内数据更新不成功,如果,底层表数据未更新,则游标内表数据可以更新

 

 

周期二:打开游标

1 OPEN  cursor_name

注意,当全局游标和局部游标变量重名时,默认会打开局部变量游标

周期三:使用游标

 游标的使用分为两步,一是操作游标在数据集内的指向,二是将游标所指向的行的部分或全部内容进行操作

 对于指定  SCROLL选项的游标来说,支持6种移动选项,分别为到第一行(FIRST),最后一行(LAST),下一行(NEXT),上一行(PRIOR),直接跳到某行(ABSOLUTE(n)),相对于目前跳几行(RELATIVE(n))

对于未指定  SCROLL选项的游标来说,只支持NEXT取值

 1 DECLARE cursor_name CURSOR scroll   -----必须指定scroll,否则只支持next只进选项
 2 FOR select PM_Equipments.NameCN from PM_Equipments
 3 open cursor_name                    -----打开游标
 4 declare @equipments nvarchar(50)    -----声明变量
 5 fetch next from cursor_name into @equipments     -----取下一行设备名称,赋值给变量(这就是使用过程:1定位游标,2赋值操作)
 6 fetch last from cursor_name into @equipments     -----取最后一行设备名称,赋值给变量
 7 fetch first from cursor_name into @equipments     -----取第一行设备名称,赋值给变量
 8 fetch prior from cursor_name into @equipments     -----取上一行设备名称,赋值给变量
 9 fetch absolute 3  from cursor_name into @equipments     -----取第三行设备名称,赋值给变量
10 fetch relative -1  from cursor_name into @equipments     -----取当前行的上一行设备名称,赋值给变量(负数向前,正数向后)

游标经常会和全局变量@@FETCH_STATUS与WHILE循环来共同使用,以达到遍历游标所在数据集的目的

@@FETCH_STATUS值有三种情况,分别是0,-1,-2。

 0 FETCH 语句成功   
-1 FETCH 语句失败或此行不在结果集中   
-2 被提取的行不存在 

 1     --1.声明游标
 2     DECLARE CreateEntry_Cursor cursor scroll
 3     FOR select ProductUniqueCode from dbo.POM_Entry where OrderCode = @OrderCode AND Status = Recovery;
 4     --2.打开游标
 5     OPEN CreateEntry_Cursor
 6     --3.声明游标提取数据所要存放的变量
 7     DECLARE @ProductUniqueCode NVARCHAR(100),@OrderIndex INT = 1
 8     --4.定位游标到哪一行
 9     FETCH First from CreateEntry_Cursor into @ProductUniqueCode  --into的变量数量必须与游标查询结果集的列数相同
10     WHILE @@fetch_status=0  --提取成功,进行下一条数据的提取操作 
11     BEGIN
12         INSERT dbo.POM_Entry
13                 ( Id ,
14                   OrderCode ,
15                   EntryCode ,
16                   ProductUniqueCode ,
17                   CellCode ,
18                   PPRCode ,
19                   Type ,
20                   Status ,
21                   PlanStartTime ,
22                   PlanEndTime ,
23                   OrderIndex,
24                   CreationTime ,
25                   CreateUserId ,
26                   IsEnabled ,
27                   IsDeleted 
28                 )
29         VALUES  ( NEWID() , -- Id - nvarchar(50)
30                   @OrderCode +  - + CONVERT(NVARCHAR(50),@index) , -- OrderCode - nvarchar(50)
31                   [dbo].[f_GetBusinessNo] (EN,01) , -- EntryCode - nvarchar(50)
32                   @ProductUniqueCode , -- ProductUniqueCode - nvarchar(100)
33                   @CellCode , -- CellCode - nvarchar(50)
34                   @PPRCode , -- PPRCode - nvarchar(50)
35                   @Type , -- Type - nvarchar(50)
36                   New , -- Status - nvarchar(50)
37                   @PlanStartTime , -- PlanStartTime - datetime
38                   @PlanEndTime , -- PlanEndTime - datetime
39                   @OrderIndex , -- OrderIndex - int
40                   GETDATE() , -- CreationTime - datetime
41                   @CreateUserId , -- CreateUserId - varchar(50)
42                   1 , -- IsEnabled - bit
43                   0  -- IsDeleted - bit
44                 )
45     FETCH next from CreateEntry_Cursor into @ProductUniqueCode --移动游标
46     SET @OrderIndex += 1;
47     END 

 

周期四:关闭游标

CLOSE test_Cursor -----关闭游标 

周期五:释放游标

1 DEALLOCATE test_Cursor  -----释放游标

六、对于游标一些优化建议

 如果能不用游标,尽量不要使用游标

 用完用完之后一定要关闭和释放

尽量不要在大量数据上定义游标

尽量不要使用游标上更新数据

尽量不要使用insensitive, static和keyset这些参数定义游标

如果可以,尽量使用FAST_FORWARD关键字定义游标

如果只对数据进行读取,当读取时只用到FETCH NEXT选项,则最好使用FORWARD_ONLY参数

 

 

Sql Server游标的使用