Sql存储过程游标循环的用法及sql如何使用cursor写一个简单的循环
用游标,和WHILE可以遍历您的查询中的每一条记录并将要求的字段传给变量进行相应的处理
==================
DECLARE @A1VARCHAR(10), @A2VARCHAR(10), @A3INT DECLARECURSORYOUCURNAMEFORSELECTA1,A2,A3FROMYOUTABLENAME OPENYOUCURNAME fetchnextfromyoucurnameinto@a1,@a2,@a3 while@@fetch_status<>-1 begin update…set…-a3where…
……您要执行的操作写在这里
fetchnextfromyoucurnameinto@a1,@a2,@a3 end closeyoucurname deallocateyoucurname
—————————————
在应用程序开发的时候,我们经常可能会遇到下面的应用,我们会通过查询数据表的记录集,循环每一条记录,通过每一条的记录集对另一张表进行数据进行操作,如插入与更新,我们现在假设有一个这样的业务:老师为所在班级的学生选课,选的课程如有哲学、马克思主义政治经济学、毛泽东思想概论、邓小平理论这些课,现在操作主要如下:
1)先要查询这些还没有毕业的这些学生的名单,毕业过后的无法进行选课;
2)在批量的选取学生的同时,还需要添加对应的某一门课程;
3)点添加后选课结束。
数据量少可能看不出用Java程序直接多次进行数据库操作这种办法实现的弱点,因为它每次在操作数据库的时候,都存在着频繁的和数据库的I/O直接交互,这点性能的牺牲实属不应该,那我们就看下面的方法,通过存储过程的游标方法来实现:建立存储过程:
CreatePROCEDUREP_InsertSubject @SubjectIdint AS DECLARErsCURSORLOCALSCROLLFOR selectstudentidfromstudentwhereStudentGradu=1 OPENrs FETCHNEXTFROMrsINTO@tempStudentID WHILE@@FETCH_STATUS=0 BEGIN InsertSelSubjectvalues(@SubjectId,@tempStudentID) FETCHNEXTFROMrsINTO@tempStudentID END CLOSErs GO
使用游标对记录集循环进行处理的时候一般操作如以下几个步骤:
1、把记录集传给游标;
2、打开游标
3、开始循环
4、从游标中取值
5、检查那一行被返回
6、处理
7、关闭循环
8、关闭游标
上面这种方法在性能上面无疑已经是提高很多了,但我们也想到,在存储过程编写的时候,有时候我们尽量少的避免使用游标来进行操作,所以我们还可以对上面的存储过程进行改造,使用下面的方法来实现:
CreatePROCEDUREP_InsertSubject @SubjectIdint AS declare@iint, @studentid DECLARE@tCanStudentTABLE ( studentidint ,FlagIDTINYINT ) BEGIN insert@tCanStudentselectstudentid,0fromstudentwhereStudentGradu=1 SET@i=1 WHILE(@i>=1) BEGIN SELECT@studentid='' SELECTTOP1@studentid=studentidFROM@tCanStudentWHEREflagID=0 SET@i=@@ROWCOUNT IF@i<=0GOTOReturn_Lab InsertSelSubjectvalues(@SubjectId,@studentid) IF@@error=0 UPDATE@tCanStudentSETflagID=1WHEREstudentid=@studentid Return_Lab: END End GO
我们现在再来分析以上这个存储过程,它实现的方法是先把满足条件的记录集数据存放到一个表变量中,并且在这个表变量中增加一个FLAGID进行数据初始值为0的存放,然后去循环这个记录集,每循环一次,就把对应的FLAGID的值改成1,然后再根据循环来查找满足条件等于0的情况,可以看到,每循环一次,处理的记录集就会少一次,然后循环的往选好课程表里面插入,直到记录集的条数为0时停止循环,此时完成操作。
比较以上的几种循环方法的应用,就会知道,有时候可能对于同一种功能我们实现的方法不同,而最终应用程序性能的影响的差异就会很大,第二种、第三种就大大的减少的数据库交互I/O操作的频繁,会节省很多时间,方法三又避免用游标又可以节省不必要的开销。
使用SQL的Agent可以执行计划任务,把写好的SQL语句放在计划任务里,可以达到奇妙的效果,如定时备份数据,定时执行特定操作等等,当涉及循环操作很多条数据时,这里就要使用游标了,当然SQL中也有循环语句,如使用While。不过while的功能只能实现一般的操作,游标的功能更为强大些,可在一个指定的一个集合内循环操作数据,实现动态操作,那就更牛了,呵呵,以下资料供存档用。
WHILE
设置重复执行SQL语句或语句块的条件。只要指定的条件为真,就重复执行语句。可以使用BREAK和CONTINUE关键字在循环内部控制WHILE循环中语句的执行。
语法
WHILEBoolean_expression {sql_statement|statement_block} [BREAK] {sql_statement|statement_block} [CONTINUE]
参数
Boolean_expression
返回TRUE或FALSE的表达式。如果布尔表达式中含有SELECT语句,必须用圆括号将SELECT语句括起来。
{sql_statement|statement_block}
Transact-SQL语句或用语句块定义的语句分组。若要定义语句块,请使用控制流关键字BEGIN和END。
BREAK
导致从最内层的WHILE循环中退出。将执行出现在END关键字后面的任何语句,END关键字为循环结束标记。
CONTINUE
使WHILE循环重新开始执行,忽略CONTINUE关键字后的任何语句。
注释
如果嵌套了两个或多个WHILE循环,内层的BREAK将导致退出到下一个外层循环。首先运行内层循环结束之后的所有语句,然后下一个外层循环重新开始执行。
示例
declare@iint set@i=1 while@i<30 begin insertintotest(userid)values(@i) set@i=@i+1 end
------------------------------------------------------------
while条件 begin 执行操作 set@i=@i+1 end
A.在嵌套的IF...ELSE和WHILE中使用BREAK和CONTINUE
在下例中,如果平均价格少于$30,WHILE循环就将价格加倍,然后选择最高价。如果最高价少于或等于$50,WHILE循环重新启动并再次将价格加倍。该循环不断地将价格加倍直到最高价格超过$50,然后退出WHILE循环并打印一条消息。
USEpubs GO WHILE(SELECTAVG(price)FROMtitles)<$30 BEGIN UPDATEtitles SETprice=price*2 SELECTMAX(price)FROMtitles IF(SELECTMAX(price)FROMtitles)>$50 BREAK ELSE CONTINUE END PRINT'Toomuchforthemarkettobear'
B.在带有游标的过程中使用WHILE
以下的WHILE结构是名为count_all_rows过程中的一部分。下例中,该WHILE结构测试用于游标的函数@@FETCH_STATUS的返回值。因为@@FETCH_STATUS可能返回–2、-1或0,所以,所有的情况都应进行测试。如果某一行在开始执行此存储过程以后从游标结果中删除,将跳过该行。成功提取(0)后将执行BEGIN...END循环内部的SELECT语句。
单变量循环
USEpubs DECLAREtnames_cursorCURSOR FOR SELECTTABLE_NAME FROMINFORMATION_SCHEMA.TABLES OPENtnames_cursor DECLARE@tablenamesysname --SET@tablename='authors' FETCHNEXTFROMtnames_cursorINTO@tablename WHILE(@@FETCH_STATUS<>-1) BEGIN IF(@@FETCH_STATUS<>-2) BEGIN SELECT@tablename=RTRIM(@tablename) EXEC('SELECT'''+@tablename+'''=count(*)FROM' +@tablename) PRINT'' END FETCHNEXTFROMtnames_cursorINTO@tablename END CLOSEtnames_cursor DEALLOCATEtnames_cursor
多变量循环
CREATEPROCEDUREmy_FeeCountAS declare@到期时间char(10) declare@片区char(20) declare@缴费用户数char(10) declare@sqlchar(2000) declarecur_datacursorfor selectconvert(varchar(10),到期时间,120)as到期时间,片区,count(distinctmain_id)as缴费用户数 fromV_aipu_feewhere提交时间>=convert(varchar(10),getdate()-90,120)and提交时间<convert(varchar(10),getdate()+1-90,120) and收费类型='续费收费' Groupbyconvert(varchar(10),到期时间,120),片区 orderbyconvert(varchar(10),到期时间,120) opencur_data fetchnextfromcur_datainto@到期时间,@片区,@缴费用户数 while(@@fetch_status=0) begin set@sql='update'+RTRIM(@片区)+'实收='+RTRIM(@片区)+'实收+'+RTRIM(@缴费用户数)+'where收费日期='''+RTRIM(@到期时间)+'''' print@sql fetchnextfromcur_datainto@到期时间,@片区,@缴费用户数 end closecur_data
下面接着给大家介绍sql使用cursor写一个简单的循环
1.排错
和sqlserver较了一天的劲,只写了两个简单的存储过程。当然智商虽然不高还没低到这个份上。大半天都是花费在排错上了。
System.Data.SqlClient.SqlException:SQLServer不存在或访问被拒绝。at
System.Data.SqlClient.ConnectionPool.GetConnection(Boolean&isInTransaction)atSystem.Data.SqlClient.SqlConnectionPoolManager.GetPooledConnection(SqlConnectionStringoptions,Boolean&isInTransaction)atSystem.Data.SqlClient.SqlConnection.Open()at_20060409.WebForm1.test()ind:\workfiles\20060409\webform1.aspx.cs:line52
冷不丁报了这个错,这个就是从程序里连不上。在服务中除了MSSQLserverADHelper以外所有的服务都打开了还是不行;名称正确、无别名、没起实例名称;端口号服务器端和客户端都是1433。为了找出问题只好上网查查结果发现连上网线后这个毛病就没了。晕,看来是少打了什么补丁。
2.写循环
首先需求是这样的我手里现在有两张表,rights和roles。
表结构如下:
rights ------------------- rightidint rightvarchar(20) ========== roles ------------------- roleidint roletypeint rolevarchar(20) rightidint
在我的role表里有一个角色admin。admin拥有right表中的所有权限,但是有四个字段,要是用手写完我这个速度,加上这个数量我看也就算了。省了手上的力气就要费脑子。
在查询分析器里写了半天,总算是吭哧出来了。
首先使用truncatetable把role原来的失败信息清掉,听说会比delete快一点,只不过数量少看不出明显效果。
然后建立一个临时的用于填充的存储过程。
createprocedureTempFill declaremycursorcursorfor selectrightidfromrights--这两个是一句,定义游标,然后从rights中取一个rightid给游标 openmycursor declare@rightidint--定义一个变量 fetchmycursorinto@rightid--把mycursor当前的值给@rightid while@@fetch_status= begin insertintorolestable(roletype,role,rightid)values(,'admin',@rightid) fetchmycursorinto@rightid end closemycursor deallocatemycursor
这样就写完了。
executeTempFill再执行他一下。