SQL Server 游标
结果集,结果集就是select查询之后返回的所有行数据的集合。
在关系数据库中,我们对于查询的思考是面向集合的。而游标打破了这一规则,游标使得我们思考方式变为逐行进行。
正常面向集合的思维方式是:
而对于游标来说:
同样的,在性能上,游标会吃更多的内存,减少可用的并发,占用宽带,锁定资源,当然还有更多的代码量……
游标的分类
根据游标检测结果集变化的能力和消耗资源的情况不同,SQL Server支持的API服务器游标分为一下4种:
静态游标
静态游标的结果集,在游标打开的时候建立在TempDB中,不论你在操作游标的时候,如何操作数据库,游标中的数据集都不会变。
例如你在游标打开的时候,对游标查询的数据表数据进行增删改,操作之后,静态游标中select的数据依旧显示的为没有操作之前的数据。
如果想与操作之后的数据一致,则重新关闭打开游标即可。
动态游标
这个则与静态游标相对,滚动游标时,动态游标反应结果集中的所有更改。
结果集中的行数据值、顺序和成员在每次提取时都会变化。
所有用户做的增删改语句通过游标均可见。
如果使用API函数或T-SQL Where Current of子句通过游标进行更新,他们将立即可见。
在游标外部所做的更新直到提交时才可见。
只进游标
只进游标不支持滚动,只支持从头到尾顺序提取数据,数据库执行增删改,在提取时是可见的,但由于该游标只能进不能向后滚动,所以在行提取后对行做增删改是不可见的。
键集驱动游标
打开键集驱动游标时,该游标中的各个成员身份和顺序是固定的。
打开游标时,结果集这些行数据被一组唯一标识符标识,被标识的列做删改时,用户滚动游标是可见的,如果没被标识的列增该,则不可见,比如insert一条数据,是不可见的,若可见,须关闭重新打开游标。
静态游标在滚动时检测不到表数据变化,但消耗的资源相对很少。
动态游标在滚动时能检测到所有表数据变化,但消耗的资源却较多。
键集驱动游标则处于他们中间,所以根据需求建立适合自己的游标,避免资源浪费。
T-SQL中游标的生命周期以及实现
在T-SQL中,游标的生命周期由5部分组成:声明游标、打开游标、读取游标数据、关闭游标、释放游标。
1. 定义一个游标
在T-SQL中,定义一个游标可以是非常简单,也可以相对复杂,取决于游标的参数。而游标的参数设置取决于你对游标原理的了解程度。
游标其实可以理解成一个定义在特定数据集上的指针,我们可以控制这个指针遍历数据集,或者仅仅是指向特定的行,所以游标是定义在以Select开始的数据集上的:
T-SQL中的游标定义在MSDN中如下:
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变量的定义方法。游标变量支持两种方式赋值,定义时赋值和先定义后赋值。
定义游标变量像定义其他局部变量一样,在游标前加”@”,注意,如果定义全局的游标,只支持定义时直接赋值,并且不能在游标名称前面加“@”,两种定义方式如下:
下面我们来看游标定义的参数:
LOCAL 和 GLOBAL 二选一
LOCAL
作用域为局部,只在定义它的批处理,存储过程或触发器中有效。
GLOBAL
作用域为全局,由连接执行的任何存储过程或批处理中,都可以引用该游标。
如果 GLOBAL 和 LOCAL 参数都未指定,则默认值由 default to local cursor 数据库选项的设置控制。
例如:
FORWARD_ONLY 和 SCROLL 二选一
FORWARD_ONLY
指定游标只能从第一行滚动到最后一行。
FETCH NEXT 是唯一支持的提取选项。
如果在指定 FORWARD_ONLY 时不指定 STATIC、KEYSET 和 DYNAMIC 关键字,则游标作为 DYNAMIC 游标进行操作。
如果 FORWARD_ONLY 和 SCROLL 均未指定,则除非指定 STATIC、KEYSET 或 DYNAMIC 关键字,否则默认为 FORWARD_ONLY。
STATIC、KEYSET 和 DYNAMIC 游标默认为 SCROLL。
SCROLL
支持游标在定义的数据集中向任何方向,或任何位置移动。
如下图:
STATIC KEYSET DYNAMIC 和 FAST_FORWARD 四选一
这四个关键字是游标所在数据集所反应的表内数据和游标读取出的数据的关系。
STATIC
定义一个游标,以创建将由该游标使用的数据的临时复本。 对游标的所有请求都从 tempdb 中的这一临时表中得到应答;因此,在对该游标进行提取操作时返回的数据中不反映对基表所做的修改,并且该游标不允许修改。
DYNAMIC
定义一个游标,以反映在滚动游标时对结果集内的各行所做的所有数据更改。
行的数据值、顺序和成员身份在每次提取时都会更改。
动态游标不支持 ABSOLUTE 提取选项。
KEYSET
指定当游标打开时,游标中行的成员身份和顺序已经固定。
对行进行唯一标识的键集内置在 tempdb 内一个称为 keyset 的表中。
对基表中的非键值所做的更改(由游标所有者更改或由其他用户提交)可以在用户滚动游标时看到。
其他用户执行的插入是不可见的(不能通过 Transact-SQL 服务器游标执行插入)。
如果删除某一行,则在尝试提取该行时返回值为 -2 的 @@FETCH_STATUS。
从游标外部更新键值类似于删除旧行后再插入新行。 具有新值的行不可见,且尝试提取具有旧值的行时返回的 @@FETCH_STATUS 为 -2。
如果通过指定 WHERE CURRENT OF 子句来通过游标执行更新,则新值可见。
注意:如果查询引用了至少一个无唯一索引的表,则键集游标将转换为静态游标。
FAST_FORWARD
指定启用了性能优化的 FORWARD_ONLY、READ_ONLY 游标。 如果指定了 SCROLL 或 FOR_UPDATE,则不能也指定 FAST_FORWARD。
注意:FAST_FORWARD 和 FORWARD_ONLY 可以同时用在同一个 DECLARE CURSOR 语句中。
READ_ONLY SCROLL_LOCKS OPTIMISTIC 三选一
READ_ONLY
禁止通过该游标进行更新。 在 UPDATE 或 DELETE 语句的 WHERE CURRENT OF 子句中不能引用该游标。 该选项优于要更新的游标的默认功能。
SCROLL_LOCKS
指定通过游标进行的定位更新或删除一定会成功。
将行读入游标时 SQL Server 将锁定这些行,以确保随后可对它们进行修改。
如果还指定了 FAST_FORWARD 或 STATIC,则不能指定 SCROLL_LOCKS。
OPTIMISTIC
指定如果行自读入游标以来已得到更新,则通过游标进行的定位更新或定位删除不成功。
当将行读入游标时,SQL Server 不锁定行。
它改用 timestamp 列值的比较结果来确定行读入游标后是否发生了修改,如果表不含 timestamp 列,它改用校验和值进行确定。 如果已修改该行,则尝试进行的定位更新或删除将失败。
如果还指定了 FAST_FORWARD,则不能指定 OPTIMISTIC。
2. 打开游标
语法
OPEN { { [ GLOBAL ] cursor_name } | cursor_variable_name }
参数
GLOBAL
指定 cursor_name 是指全局游标。
cursor_name
已声明的游标的名称。
如果全局游标和局部游标都使用 cursor_name 作为其名称,那么如果指定了 GLOBAL,则 cursor_name指的是全局游标;否则 cursor_name 指的是局部游标。
cursor_variable_name
游标变量的名称,该变量引用一个游标。
3. 使用游标
游标的使用分为两部分,一部分是操作游标在数据集内的指向,另一部分是将游标所指向的行的部分或全部内容进行操作。
语法
FETCH
[ [ NEXT | PRIOR | FIRST | LAST
| ABSOLUTE { n | @nvar }
| RELATIVE { n | @nvar }
]
FROM
]
{ { [ GLOBAL ] cursor_name } | @cursor_variable_name }
[ INTO @variable_name [ ,...n ] ]
参数
只支持6种移动选项:
FIRST
结果集的第一行。
LAST
结果集的最后一行。
NEXT
当前位置的下一行。
PRIOR
当前位置的上一行。
ABSOLUTE(n)
从游标的第一行开始数,直接跳到第n行。
如果n为正整数,则读取第n条记录
如果n为负数,则倒数提取第n条记录
如果n为0,则不读取任何记录
RELATIVE(n)
相对于当前位置数,跳第n行。
如果n为正整数,则读取上次读取记录之后第n条记录
如果n为负数,则读取上次读取记录之前第n条记录
如果n为0,则读取上次读取的记录
提取数据
例如:
fetch first from orderNum_02_cursor
fetch relative 3 from orderNum_02_cursor
fetch next from orderNum_02_cursor
fetch absolute 4 from orderNum_02_cursor
fetch next from orderNum_02_cursor
fetch last from orderNum_02_cursor
fetch prior from orderNum_02_cursor
select * from bigorder where orderNum='ZEORD003402'
提取数据赋值给变量
declare @OrderId int
fetch absolute 3 from orderNum_02_cursor into @OrderId
select @OrderId as id
select * from bigorder where orderNum='ZEORD003402'
@@FETCH_STATUS
通过检测全局变量@@Fetch_Status的值,获得提取状态信息,该状态用于判断Fetch语句返回数据的有效性。
当执行一条Fetch语句之后,@@Fetch_Status可能出现3种值:
0:Fetch语句成功。
-1:Fetch语句失败或行不在结果集中。
-2:提取的行不存在。
这个状态值可以帮你判断提取数据的成功与否。
游标经常会和全局变量@@FETCH_STATUS 与 WHILE 循环来共同使用,以达到遍历游标所在数据集的目的,例如:
declare @OrderId int
fetch absolute 3 from orderNum_02_cursor into @OrderId
while @@fetch_status=0 --提取成功,进行下一条数据的提取操作
begin
select @OrderId as id
fetch next from orderNum_02_cursor into @OrderId --移动游标
end
利用游标更新删除数据
--游标修改当前数据语法
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='' 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
4. 关闭游标
游标打开后,服务器会专门为游标分配一定的内存空间存放游标操作的数据结果集,同时使用游标也会对某些数据进行封锁。所以游标一旦用过,应及时关闭,避免服务器资源浪费。
语法
CLOSE { { [ GLOBAL ] cursor_name } | cursor_variable_name }
参数
GLOBAL
指定cursor_name 是指全局游标。
cursor_name
打开的游标的名称。 如果全局游标和局部游标都使用 cursor_name 作为它们的名称,那么当指定 GLOBAL 时,cursor_name 指的是全局游标;其他情况下,cursor_name 指的是局部游标。
cursor_variable_name
与打开的游标关联的游标变量的名称。
5. 释放游标
删除游标,释放资源。
语法
DEALLOCATE { { [ GLOBAL ] cursor_name } | @cursor_variable_name }
参数
cursor_name
已声明游标的名称。
如果全局游标和局部游标都使用 cursor_name 作为它们的名称,那么指定 GLOBAL 时,cursor_name 指的是全局游标;未指定 GLOBAL 时,则指的是局部游标。
@cursor_variable_name
cursor 变量的名称。
@cursor_variable_name 的数据类型必须为 cursor。
对于游标一些优化建议:
如果能不用游标,尽量不要使用游标
用完用完之后一定要关闭和释放
尽量不要在大量数据上定义游标
尽量不要使用游标上更新数据
尽量不要使用insensitive,static和keyset这些参数定义游标
如果可以,尽量使用FAST_FORWARD关键字定义游标
如果只对数据进行读取,当读取时只用到FETCH NEXT选项,则最好使用FORWARD_ONLY参数
总结
本文从游标的基本概念,到生命周期来谈游标。游标是非常邪恶的一种存在,使用游标经常会比使用面向集合的方法慢2-3倍,当游标定义在大数据量时,这个比例还会增加。如果可能,尽量使用while,子查询,临时表,函数,表变量等来替代游标,记住,游标永远只是你最后无奈之下的选择,而不是首选。
游标是邪恶的!
SQL Server 游标的更多相关文章
- SQL Server 游标运用:鼠标轨迹字符串分割
一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 背景(Contexts) 游标模板(Cursor Template) 鼠标轨迹字符串分割SQL脚本实现(SQL Code ...
- sql server 游标的简单用法
sql server游标: --定义游标 declare cursor1 cursor for select ID,Name from A --打开游标 open cursor1 declare @i ...
- SQL Server游标 C# DataTable.Select() 筛选数据 什么是SQL游标? SQL Server数据类型转换方法 LinQ是什么? SQL Server 分页方法汇总
SQL Server游标 转载自:http://www.cnblogs.com/knowledgesea/p/3699851.html. 什么是游标 结果集,结果集就是select查询之后返回的所 ...
- SQL Server 游标运用:查看所有数据库所有表大小信息(Sizes of All Tables in All Database)
原文:SQL Server 游标运用:查看所有数据库所有表大小信息(Sizes of All Tables in All Database) 一.本文所涉及的内容(Contents) 本文所涉及的内容 ...
- SQL Server 游标运用:查看一个数据库所有表大小信息(Sizes of All Tables in a Database)
原文:SQL Server 游标运用:查看一个数据库所有表大小信息(Sizes of All Tables in a Database) 一.本文所涉及的内容(Contents) 本文所涉及的内容(C ...
- SQL Server游标(转)
清晰地介绍了SQL游标,很好的学习资料. 转自 http://www.cnblogs.com/knowledgesea/p/3699851.html 什么是游标 结果集,结果集就是select查询之后 ...
- SQL Server游标
什么是游标 结果集,结果集就是select查询之后返回的所有行数据的集合. 游标则是处理结果集的一种机制吧,它可以定位到结果集中的某一行,多数据进行读写,也可以移动游标定位到你所需要的行中进行操作数据 ...
- SQL Server游标【转】
什么是游标 结果集,结果集就是select查询之后返回的所有行数据的集合. 游标则是处理结果集的一种机制吧,它可以定位到结果集中的某一行,多数据进行读写,也可以移动游标定位到你所需要的行中进行操作 ...
- SQL SERVER 游标的使用
首先,关于什么是游标大家可以看看这篇文章,介绍得非常详细!! SQL Server基础之游标 下面是我自己的应用场景-- 有个需求,需要把数据库表里面某一个字段的值设为随机不重复的值. 表是这样的: ...
随机推荐
- zImage和uImage的区别
http://blog.csdn.net/maojudong/article/details/4178118 zImage和uImage的区别 一.vmlinuz vmlinuz是可引导的.压缩的内核 ...
- JAVA 抛出与声明异常
在编程过程中,我们往往会遇到这种情况,在当前环境中无法解决,比如用户传入的参数错误,IO设备问题等.此时,就要从当前环境中抛出异常提交给上级来处理. 在JAVA语言中,使用throw关键字来抛出异常. ...
- C#反射之基础应用
今天把反射的东西整理了一下 , 提供了最全面的东西 , 当然也是基础的东西 ,在学好了这一切的基础上 , 大家可以学习反射的具体插件等应用 首先我们建立一个类库 , 将它生成为 reflectPrj ...
- jQuery 分步引导 插件
转自:http://blog.libnav.com/js/57.html 很多时候一个网站或者一个Web应用出品,为了让你的用户知道你的站点(或应用)有些什么?如何操作?为了让你的用户有更好的体验.往 ...
- (转载)在mysql中,column 'id' in field list is ambiguous
(转载)http://blog.chinaunix.net/uid-20665047-id-3137284.html column 'id' in field list is ambiguous 这个 ...
- selenium webdriver(2)---页面对象定位
webdriver的元素定位很灵活,提供了多种定位方式: Id LinkText PartialLinkText Name TagName Xpath ClassName CssSelector 这些 ...
- gnome设置dvorak键盘布局
若桌面环境为gnome,设置Dvorak键盘程序员布局很简单.系统设置 -> 键盘 -> 布局设置 -> 点击"+" ->选择"英语(适合程序员的 ...
- Java String的== 与 equals小结
package hashcode; public class LearnString { public static void main(String[] args) { //1.先在栈中创建一个对S ...
- 27 Best Free Eclipse Plug-ins for Java Developer to be Productive
Eclipse offers an integrated development environment having an extensible plug-in system. This enabl ...
- Tornado源码探寻(请求到来)
上一篇中介绍了tornado框架在客户端请求之前所做的准备(下图1.2部分),本质上就是创建了一个socket服务端,并进行了IP和端口的绑定,但是未执行 socket的accept方法,也就是未获取 ...