SQL Server中的游标介绍

游标是面向行的,它会使开发人员变懒,懒得去想用面向集合的查询方式实现某些功能。

在性能上,游标会吃更多的内存,减少可用的并发,占用带宽,锁定资源,当然还有更多的代码量。用一个比喻来说明为什么游标会占用更多的资源。当你从ATM机取款的时候,是一次取1000的效率更高呢,还是10次100呢?

游标是非常邪恶的一种存在,使用游标经常会比使用面向集合的方法慢2-3倍,当游标定义在大数据量时,这个比例还会增加。如果可能,尽量使用while,子查询,临时表,函数,表变量等来替代游标,记住,游标永远只是你最后无奈之下的选择,而不是首选。

既然游标那么多缺点,为什么要学习游标呢?

  • 现存系统有一些游标,我们查询必须通过游标来实现。
  • 作用一个备用方式,当使用while、子查询,临时表,表变量,自建函数或其他方式仍然无法实现某些查询的时候,可以使用游标实现。

游标的定义语法:

declare cursor_name cursor [ local | global ]
     [ forward_only | scroll ]
     [ static | keyset | dynamic | fast_forward ]
     [ read_only | scroll_locks | optimistic ]
     [ type_warning ]
     for select_statement
     [ for update [ of column_name [ ,...n ] ] ]
[;]

一、定义游标

在T-SQL中,定义一个游标可以使非常简单,也可以相对复杂,这主要取决于游标的参数。而游标的参数设置取决于你对游标原理的了解程度。 
游标其实可以理解成一个定义在特定数据集上的指针,我们可以控制这个指针遍历数据集,或者仅仅是指向特定的行,所以游标是定义在以SELECT开始的数据集上的。

游标分为游标类型和游标变量。

游标变量支持两种方式赋值,定义时赋值和先定义后赋值,定义游标变量像定义其他局部变量一样,在游标前加”@”。

注意,如果定义全局的游标,只支持定义时直接赋值,并且不能在游标名称前面加“@”。

两种定义方式如下:

--定义后直接赋值
declare test_Cursor cursor for
    select * from Person;

--先定义后赋值
declare @TEST_Cursor2 cursor;

set @TEST_Cursor2 = cursor for
    select * from Person;

参数解释:

1、LOCAL和GLOBAL二选一

如果不指定游标作用域,默认作用域为GLOBAL。

--全局游标,跨GLOBAL
declare test_Cursor cursor global for
    select * from Person;

--局部游标,跨LOCAL
declare test_Cursor2 cursor local for
    select * from Person;

go --用GO结束上面的作用域

open test_Cursor;
open test_Cursor2; --此行代码报错,报游标不存在,因此可以理解局部游标不跨批处理,批处理结束后,将被隐式释放,无法在其他批处理中调用

2、FORWARD_ONLY 和 SCROLL 二选一

  • FORWARD_ONLY意味着游标只能从数据集开始向数据集结束的方向读取,FETCH NEXT是唯一的选项。默认为Forward_Only。
  • SCROLL支持游标在定义的数据集中向任何方向,或任何位置移动。
--不加参数,默认为Forward_Only
declare test_Cursor cursor for
    select * from Person;

--加Forward_Only
declare test_Cursor2 cursor forward_only for
    select * from Person;

--加SCROLL
declare test_Cursor3 cursor scroll for
    select * from Person;

open test_Cursor;
open test_Cursor2;
open test_Cursor3;

fetch last from test_Cursor; --报错 fetch: 提取类型 last 不能与只进游标一起使用。
fetch last from test_Cursor2; --报错 fetch: 提取类型 last 不能与只进游标一起使用。
fetch last from test_Cursor3; --正确执行

3、游标的分类:STATIC、 KEYSET 、DYNAMIC 和 FAST_FORWARD 四选一

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

  • STATIC:当游标被建立时,将会创建FOR后面的SELECT语句所包含数据集的副本存入tempdb数据库中,任何对于底层表内数据的更改不会影响到游标内容。
  • DYNAMIC:和STATIC完全相反的选项,当底层数据库更改时,游标的内容也会随之得到反映,在下一次fecth中,数据内容会随之更改。
  • KEYSET:可以理解为介于STATIC和DYNAMIC的折中方案,将游标所在结果集的唯一能确定每一行的主键存入tempdb,当结果集中任何行改变或者删除时,@@FETCH_STATUS会为-2,KEYSET无法探测新加入的数据。
  • FAST_FORWARD:可以理解为FORWARD_ONLY的优化版本。FORWARD_ONLY执行的是静态计划,而FAST_FORWARD是根据情况进行选择采用动态计划还是静态计划,大多数情况下FAST_FORWARD要比FORWARD_ONLY性能略好。

4、READ_ONLY 、 SCROLL_LOCKS 和 OPTIMISTIC 三选一

  • READ_ONLY:意味着声明的游标只能读取数据,游标不能做任何更新操作 。
  • SCROLL_LOCKS:是另一种极端,将读入游标的所有数据进行锁定,防止其他程序进行更改,以确保更新的绝对成功。
  • OPTIMISTIC:相对比较好的一个选择,OPTIMISTIC不锁定任何数据,当需要在游标中更新数据时,如果底层表数据更新,则游标内数据更新不成功,如果,底层表数据未更新,则游标内表数据可以更新。

5、For Update[of column_name ,....] :定义游标中可更新的列。

二、打开游标

当定义完游标后,游标需要打开后使用,只需一行代码便可打开游标:

OPEN test_Cursor

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

三、使用游标

1、利用游标提取数据

游标的使用分为两部分,一部分是操作游标在数据集内的指向,另一部分是将游标所指向的行的部分或全部内容进行操作。 
支持6种移动导航,分别为:

  • 第一行(FIRST)
  • 最后一行(LAST)
  • 下一行(NEXT)
  • 上一行(PRIOR)
  • 直接跳到某行(ABSOLUTE(n))
  • 相对于目前跳几行(RELATIVE(n))

例如:

declare test_Cursor cursor scroll for
    select name from Person;

open test_Cursor;

declare @c nvarchar(10);

--取下一行
fetch next from test_Cursor into @c;
print @c;

--取最后一行
fetch last from test_Cursor into @c;
print @c;

--取第一行
fetch first from test_Cursor into @c;
print @c;

--取上一行
fetch prior from test_Cursor into @c;
print @c;

--取第三行
fetch absolute 3 from test_Cursor into @c;
print @c;

--取相对目前来说上一行
fetch relative -1 from test_Cursor into @c;
print @c;

对于未指定SCROLL选项的游标来说(未指定,则是只进游标),只支持NEXT取值。

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

当执行一条Fetch语句之后,@@Fetch_Status可能出现3种值:

  • 0,Fetch语句成功。
  • -1:Fetch语句失败或行不在结果集中。
  • -2:提取的行不存在。

游标总记录数 @@CURSOR_ROWS

例如:

declare test_Cursor cursor fast_forward  for
    select id, name from Person;

open test_Cursor;

declare @id int;
declare @name nvarchar(10);

fetch next from test_Cursor into @id, @name;

while @@FETCH_STATUS = 0
    begin
        print @id;
        print @name;

        fetch next from test_Cursor into @id, @name;
    end;

close test_Cursor;
deallocate test_Cursor;

2、利用游标更新删除数据

游标修改当前行数据语法:

Update 基表名 Set 列名=值[,...] Where Current of 游标名

游标删除当前数行据语法:

Delete 基表名  Where Current of 游标名

举例:

---1.声明游标
declare orderNum_03_cursor cursor scroll for
    select OrderId, userId from bigorder where orderNum = 'ZEORD003402';

--2.打开游标
open orderNum_03_cursor;

--3.声明游标提取数据所要存放的变量
declare @OrderId int, @userId varchar(15);

--4.定位游标到哪一行
fetch first from orderNum_03_cursor  into @OrderId, @userId; -- into的变量数量必须与游标查询结果集的列数相同

while @@fetch_status = 0 --提取成功,进行下一条数据的提取操作
    begin
        if @OrderId = 122182
            begin
                update bigorder set UserId = '123' where current of orderNum_03_cursor; --修改当前行
            end;

        if @OrderId = 154074
            begin
                delete bigorder where current of orderNum_03_cursor; --删除当前行
            end;

        fetch next from orderNum_03_cursor
        into @OrderId, @userId; --移动游标
    end;

close orderNum_03_cursor;
deallocate orderNum_03_cursor;

四、关闭游标

在游标使用完之后,一定要记得关闭,只需要一行代码:CLOSE+游标名称

close  test_Cursor

五、释放游标

当游标不再需要被使用后,释放游标,只需要一行代码:DEALLOCATE+游标名称

deallocate test_Cursor

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

  • 如果能不用游标,尽量不要使用游标
  • 用完之后一定要关闭和释放
  • 尽量不要在大量数据上定义游标
  • 尽量不要使用游标上更新数据
  • 尽量不要使用insensitive, static和keyset这些参数定义游标
  • 如果可以,尽量使用FAST_FORWARD关键字定义游标
  • 如果只对数据进行读取,当读取时只用到FETCH NEXT选项,则最好使用FORWARD_ONLY参数

到此这篇关于SQL Server游标的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • sqlserver 游标的简单示例

    Declare @Id varchar(20) Declare @Name varchar(20) Declare Cur Cursor For select substring(id,0,7) as id,name from temp1 Open Cur Fetch next From Cur Into @Id,@Name While @@fetch_status=0 Begin Update temp Set [c3]=@Name where [id] like @Id+'%' Fetch

  • SQL Server游标的使用/关闭/释放/优化小结

    游标是邪恶的! 在关系数据库中,我们对于查询的思考是面向集合的.而游标打破了这一规则,游标使得我们思考方式变为逐行进行.对于类C的开发人员来着,这样的思考方式会更加舒服. 正常面向集合的思维方式是: 而对于游标来说: 这也是为什么游标是邪恶的,它会使开发人员变懒,懒得去想用面向集合的查询方式实现某些功能. 同样的,在性能上,游标会吃更多的内存,减少可用的并发,占用宽带,锁定资源,当然还有更多的代码量-- 从游标对数据库的读取方式来说,不难看出游标为什么占用更多的资源,打个比方: 当你从ATM取钱

  • SQL Server 游标语句 声明/打开/循环实例

    SQL Server游标语句使用方法: 复制代码 代码如下: --声明一个游标 DECLARE MyCursor CURSOR FOR SELECT TOP 5 FBookName,FBookCoding FROM TBookInfo//定义一个叫MyCursor的游标,存放for select 后的数据 --打开一个游标 OPEN MyCursor//即打开这个数据集 --循环一个游标 DECLARE @BookName nvarchar(2000),@BookCoding nvarchar(

  • MS SQL Server游标(CURSOR)的学习使用

    说实的,使用MS SQL Server这样久,游标一直没有使用过.以前实现相似的功能,都是使用WHILE循环加临时表来实现.刚才有参考网上示例练习写了一下.了解到游标概念与语法. 下面代码示例中, 先是宣告你在游标中需使用变量,也就是临时存储处理字段的数据. 2. 宣告一个游标,并SELECT需要处理的数据集. 3. 打开游标(#8行代码). 4. 从游标中拿来FETCH NEXT 数据给变量赋值. 5. 循环@@FETCH_STATUS = 0条件. 6. 在循环块,可以处理第一笔的记录逻辑了

  • SQL Server游标的介绍与使用

    游标概念 数据库操作中我们常常会遇到这样情况,即从某一结果集中逐一地读取一条记录.那么如何解决这种问题呢?游标为我们提供了一种极为优秀的解决方案. 游标(cursor)是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果. 每个游标区都有一个名字.用户可以用SQL语句逐一从游标中获取记录,并赋给主变量,交由主语言进一步处理.游标提供了一种对从表中检索出的数据进行操作的灵活手段,就本质而言,游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制. 游标总是与一条SQL 查询语句

  • SQL Server中的游标介绍

    游标是面向行的,它会使开发人员变懒,懒得去想用面向集合的查询方式实现某些功能. 在性能上,游标会吃更多的内存,减少可用的并发,占用带宽,锁定资源,当然还有更多的代码量.用一个比喻来说明为什么游标会占用更多的资源.当你从ATM机取款的时候,是一次取1000的效率更高呢,还是10次100呢? 游标是非常邪恶的一种存在,使用游标经常会比使用面向集合的方法慢2-3倍,当游标定义在大数据量时,这个比例还会增加.如果可能,尽量使用while,子查询,临时表,函数,表变量等来替代游标,记住,游标永远只是你最后

  • SQL Server中的事务介绍

    事务全部是关于原子性的.原子性的概念是指可以把一些事情当做一个单元来看待.从数据库的角度看,它是指应全部执行或全部都不执行的一条或多条语句的最小组合.为了理解事务的概念,需要能够定义非常明确的边界.事务要有非常明确的开始和结束点.SQL Server中的每一条SELECT.INSERT.UPDATE和DELETE语句都是隐式事务的一部分.即使只发出一条语句,也会把这条语句当做一个事务-要么执行语句中的所有内容,要么什么都不执行.但是如果需要的不只是一条,可能是多条语句呢?在这种情况下,就需要有一

  • SQL Server中T-SQL标识符介绍与无排序生成序号的方法

    T-SQL 标识符 在T-SQL语言中,对SQLServer数据库及其数据对象(比如表.索引.视图.存储过程.触发器等)需要以名称来进行命名并加以区分,这些名称就称为标识符. 通常情况下,SQLServer数据库.数据库以及各种数据对象都应该有一个标识符,但对于某些对象来说,比如约束,标识符是可选的.推荐每个对象都使用标识符. 1.常规标识符的规则 (1).首字符:标识符的第一个字符必须满足下列条件: Unicode标准3.2定义的字母,通常就是字母a~z和A~Z. 下划线(_).at字符(@)

  • Sql Server中的视图介绍

    一):视图的定义 视图是由基于一个或多个表的一个查询所定义的虚拟表,它将查询的具体定义保存起来,视图不包含任何数据. 视图在操作上和数据表没有什么区别,但两者的差异是在其本质上的.数据表是实际存储记录的地方,然而视图并不是保存任何记录,它存储的实际上是查询语句. 相同的数据表,根据不同用户的不同需求,可以创建不同的视图. 二):视图的作用 1):筛选表中的行 2):防止未经许可的用户访问敏感数据 3):降低数据库的复杂程度 4):将多个物理数据抽象为一个逻辑数据库 三):创建视图实例 复制代码

  • Sql Server中的事务介绍

    1.什么是事务:事务是一个不可分割的工作逻辑单元,在数据库系统上执行并发操作时事务是做为最小的控制单元来使用的. 他包含的所有数据库操作命令作为一个整体一起向系提交或撤消,这一组数据库操作命令要么都执行,要么都不执行. 事务是一个不可分割的工作逻辑单元 2.事务的分类.   按事务的启动与执行方式,可以将事务分为3类:   ①显示事务 :也称之为用户定义或用户指定的事务,即可以显式地定义启动和结束的事务.分布式事务属于显示事务   ②自动提交事务:默认事务管理模式.如果一个语句成功地完成,则提交

  • SQL Server中常用截取字符串函数介绍

    SQL Server中一共提供了三个字符串截取函数:LEFT().RIGHT().SUBSTRING(). 一.LEFT()函数 函数说明如下: 语法:LEFT(character,integer). 参数介绍:参数1:要截取的字符串,参数2:截取字符个数. 返回值:返回从字符串左边开始指定个数的字符. 示例SQL:select LEFT('SQLServer_2012',3). 返回:SQL. 二.RIGHT()函数 函数说明如下: 语法:RIGHT(character,integer). 参

  • SQL Server中的文件和文件组介绍

    文件和文件组简介 在SQL Server中,数据库在硬盘上的存储方式和普通文件在Windows中的存储方式没有什么不同,仅仅是几个文件而已.SQL Server通过管理逻辑上的文件组的方式来管理文件. SQL Server通过文件组对数据文件进行管理.我们看到的逻辑数据库由一个或者多个文件组构成. 结构图如下: 文件组管理着磁盘上的文件,文件中存放的就是SQL Server的实际数据. 为什么通过文件组来管理文件 从用户的角度来说,创建对象时需要指定存储文件组的只有三种数据对象:表,索引和大对象

  • SQL Server中row_number函数用法入门介绍

    目录 一.SQL Server Row_number函数简介 二.Row_number函数的具体用法 1.使用row_number()函数对结果集进行编号 2.对结果集按照指定列进行分组,并在组内按照指定列排序 3.对结果集按照指定列去重 总结 一.SQL Server Row_number函数简介 ROW_NUMBER()是一个Window函数,它为结果集的分区中的每一行分配一个连续的整数. 行号以每个分区中第一行的行号开头. 语法实例: select *,row_number() over(

  • SQL Server 中 RAISERROR 的用法详细介绍

    SQL Server 中 RAISERROR 的用法 raiserror 的作用: raiserror 是用于抛出一个错误.[ 以下资料来源于sql server 2005的帮助 ] 其语法如下: RAISERROR ( { msg_id | msg_str | @local_variable } { ,severity ,state } [ ,argument [ ,...n ] ] ) [ WITH option [ ,...n ] ] 简要说明一下: 第一个参数:{ msg_id | ms

  • SQL Server中的RAND函数的介绍和区间随机数值函数的实现

    工作中会遇到SQL Server模拟数据生成以及数值列值(如整型.日期和时间数据类型)随机填充等等任务,这些任务中都要使用到随机数.鉴于此,本文将对SQL Server中随机数的使用简单做个总结 . T-SQL 随机有关的三个函数 RAND([seed] 此函数生成从0到1之间随机 float 值(详细说明查看https://technet.microsoft.com/zh-cn/library/ms177610(v=sql.90).aspx). CHECKSUM ( * | expressio

随机推荐