游标是面向行的,它会使开发人员变懒,懒得去想用面向集合的查询方式实现某些功能。
在性能上,游标会迟更多的内存,减少可用的并发,占用带宽,锁定资源,当然还有更多的代码量。
用一个比喻来说明为什么游标会占用更多的资源。当你从ATM机取款的时候,是一次取1000的效率更高呢,还是10次100呢?
游标的定义语法:
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 ] ] ] [;]
1、定义一个游标
在T-SQL中,定义一个游标可以使非常简单,也可以相对复杂,这主要取决于游标的参数。而游标的参数设置取决于你对游标原理的了解程度。
游标其实可以理解成一个定义在特定数据集上的指针,我们可以控制这个指针遍历数据集,或者仅仅是指向特定的行,所以欧标是定义在以SELECT开始的数据集上的。
游标分为游标类型和游标变量,对于游标变量来说,遵循T-SQL变量的定义方法。游标变量支持两种方式赋值,定义时赋值和先定义后赋值,定义游标变量像定义其他局部变量一样,在游标前加”@”,注意,如果定义全局的游标,只支持定义时直接赋值,并且不能在游标名称前面加“@”,两种定义方式如下:
--定义后直接复制 DECLARE test_Cursor CURSOR FOR SELECT * FROM Person --先定义后复制 DECLARE @TEST_Cursor2 CURSOR SET @test_Cursor2 = CURSOR FOR SELECT * FROM Person
参数解释:
1、LOCAL和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 --此行代码报错,报游标不存在,因此可以理解局部游标不跨批处理,批处理结束后,将被隐式释放,无法在其他批处理中调用
如果不指定游标作用域,默认作用域为GLOBAL。
2、FORWARD_ONLY 和 SCROLL 二选一
FORWARD_ONLY意味着游标只能从数据集开始向数据集结束的方向读取,FETCH NEXT是唯一的选项,而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 四选一
这四个关键字是游标所在数据集所反映的表数据和游标读取出数据的关系
4、READ_ONLY SCROLL_LOCKS OPTIMISTIC 三选一
当定义完游标后,游标需要打开后使用,只需一行代码便可打开游标:
OPEN test_Cursor
注意,当全局游标和局部游标变量重名时,默认会打开局部变量游标。
游标的使用分为两部分,一部分是操作游标在数据集内的指向,另一部分是将游标所指向的行的部分或全部内容进行操作。
只有支持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循环来共同使用,以达到遍历游标所在数据集的目的。
例如:
DECLARE test_Cursor CURSOR SCROLL FOR SELECT id,name FROM Person OPEN test_Cursor DECLARE @id int DECLARE @name NVARCHAR(10) WHILE @@FETCH_STATUS = 0 BEGIN PRINT @id PRINT @nem FETCH NEXT FROM test_Cursor INTO @id,@name END CLOSE test_Cursor DEALLOCATE test_Cursor
在游标使用完之后,一定要记得关闭,只需要一行代码:CLOSE+游标名称
CLOSE test_Cursor
当游标不再需要被使用后,释放游标,只需要一行代码:DEALLOCATE+游标名称
DEALLOCATE test_Cursor
到生命周期来谈游标。游标是非常邪恶的一种存在,使用游标经常会比使用面向集合的方法慢2-3倍,当游标定义在大数据量时,这个比例还会增加。如果可能,尽量使用while,子查询,临时表,函数,表变量等来替代游标,记住,游标永远只是你最后无奈之下的选择,而不是首选。