在编写SQL批处理或存储过程代码的过程中,经常会碰到有些业务逻辑的处理,需要对满足条件的数据记录逐行进行处理,这个时候,大家首先想到的方案大部分是用“游标”进行处理。

  举个例子,在订单管理系统中,客服需要对订单日期为2012-09-01的销售订单进行某个批量操作,比如批量发货操作,后台业务逻辑处理时,需要对满足条件的订单记录进行逐行处理。

  我首先是采用“游标”编写的业务逻辑存储过程,SQL代码可以如下:

游标
DECLARE @ORDERID VARCHAR(30) -- 声明局部游标:从订单数据表获取订单日期为2012-09-01,订单类型为Sales的订单编号
DECLARE CURSOR_ORDER CURSOR LOCAL FOR
SELECT ORDERID FROM ORDERHD H WHERE ORDERDATE = '2012-09-01' AND H.ORDERTYPE = 'Sales' -- 打开游标
OPEN CURSOR_ORDER
FETCH NEXT FROM CURSOR_ORDER INTO @ORDERID
WHILE @@FETCH_STATUS = 0
BEGIN /*
此处编写对当前行数据的业务逻辑处理代码
*/ -- 得到下一条记录
FETCH NEXT FROM CURSOR_ORDER INTO @ORDERID
END -- 关闭游标
CLOSE CURSOR_ORDER
-- 释放游标
DEALLOCATE CURSOR_ORDER

功能是实现了,但是客服在实际使用过程中,经常反馈批量操作效率太慢,需要等待较长时间才能完成操作。经过测试发现,速度慢在游标逐行处理过程中,当需要处理的记录数较大,而且游标处理位于数据库事务内时,速度非常慢。

  那么,有什么方法可以解决这个处理速度慢的问题吗?

  经不断的尝试,终于找到一个方法,那就是用WHILE循环来进行逐行数据处理。首先将需要处理的数据记录获取到一个临时表(此临时表包括2个重要字段:REFID - 记录行号,DealFlg:行处理标识,用1/0标识行是否已处理),然后WHILE循环对临时表进行逐行处理,SQL代码如下:

While 循环
DECLARE @REFID INT
, @ORDERID VARCHAR(30) -- 获取待处理的数据记录到临时表
-- 字段说明:REFID:记录行号 / DealFlg:行处理标识
SELECT REFID = IDENTITY(INT , 1, 1), DealFlg = 0, ORDERID
INTO #Temp_Lists
FROM ORDERHD
WHERE ORDERDATE = '2012-09-01' AND H.ORDERTYPE = 'Sales' -- 获取临时表数据的最小行号
SELECT @REFID = MIN(REFID) FROM #Temp_Lists WHERE DealFlg = 0 -- 若最小行号不为空(有需要处理的数据)
WHILE @REFID IS NOT NULL
BEGIN -- 获取当前处理行的信息
SELECT @ORDERID = ORDERID FROM #Temp_Lists WHERE REFID = @REFID /*
此处编写对当前行数据的业务逻辑处理代码
*/ -- 标识当前行已处理完毕
UPDATE #Temp_Lists SET DealFlg = 1 WHERE REFID = @REFID -- 选择下一行号
SELECT @REFID = MIN(REFID) FROM #Temp_Lists WHERE DealFlg = 0 AND REFID > @REFID END

经过这样对原存储过程进行修正后,批量操作速度得到显著提升。

  有兴趣的朋友,可以尝试使用这个方法替代游标,对比2种方案的处理效率。

在编写SQL批处理或存储过程代码的过程中,经常会碰到有些业务逻辑的处理,需要对满足条件的数据记录逐行进行处理,这个时候,大家首先想到的方案大部分是用“游标”进行处理。

  举个例子,在订单管理系统中,客服需要对订单日期为2012-09-01的销售订单进行某个批量操作,比如批量发货操作,后台业务逻辑处理时,需要对满足条件的订单记录进行逐行处理。

  我首先是采用“游标”编写的业务逻辑存储过程,SQL代码可以如下:

 1 DECLARE @ORDERID    VARCHAR(30)
2
3 -- 声明局部游标:从订单数据表获取订单日期为2012-09-01,订单类型为Sales的订单编号
4 DECLARE CURSOR_ORDER CURSOR LOCAL FOR
5 SELECT ORDERID FROM ORDERHD H WHERE ORDERDATE = '2012-09-01' AND H.ORDERTYPE = 'Sales'
6
7 -- 打开游标
8 OPEN CURSOR_ORDER
9 FETCH NEXT FROM CURSOR_ORDER INTO @ORDERID
10 WHILE @@FETCH_STATUS = 0
11 BEGIN
12
13 /*
14 此处编写对当前行数据的业务逻辑处理代码
15 */
16
17 -- 得到下一条记录
18 FETCH NEXT FROM CURSOR_ORDER INTO @ORDERID
19 END
20
21 -- 关闭游标
22 CLOSE CURSOR_ORDER
23 -- 释放游标
24 DEALLOCATE CURSOR_ORDER

  功能是实现了,但是客服在实际使用过程中,经常反馈批量操作效率太慢,需要等待较长时间才能完成操作。经过测试发现,速度慢在游标逐行处理过程中,当需要处理的记录数较大,而且游标处理位于数据库事务内时,速度非常慢。

  那么,有什么方法可以解决这个处理速度慢的问题吗?

  经不断的尝试,终于找到一个方法,那就是用WHILE循环来进行逐行数据处理。首先将需要处理的数据记录获取到一个临时表(此临时表包括2个重要字段:REFID - 记录行号,DealFlg:行处理标识,用1/0标识行是否已处理),然后WHILE循环对临时表进行逐行处理,SQL代码如下:

 1 DECLARE   @REFID        INT
2 , @ORDERID VARCHAR(30)
3
4 -- 获取待处理的数据记录到临时表
5 -- 字段说明:REFID:记录行号 / DealFlg:行处理标识
6 SELECT REFID = IDENTITY(INT , 1, 1), DealFlg = 0, ORDERID
7 INTO #Temp_Lists
8 FROM ORDERHD
9 WHERE ORDERDATE = '2012-09-01' AND H.ORDERTYPE = 'Sales'
10
11 -- 获取临时表数据的最小行号
12 SELECT @REFID = MIN(REFID) FROM #Temp_Lists WHERE DealFlg = 0
13
14 -- 若最小行号不为空(有需要处理的数据)
15 WHILE @REFID IS NOT NULL
16 BEGIN
17
18 -- 获取当前处理行的信息
19 SELECT @ORDERID = ORDERID FROM #Temp_Lists WHERE REFID = @REFID
20
21 /*
22 此处编写对当前行数据的业务逻辑处理代码
23 */
24
25 -- 标识当前行已处理完毕
26 UPDATE #Temp_Lists SET DealFlg = 1 WHERE REFID = @REFID
27
28 -- 选择下一行号
29 SELECT @REFID = MIN(REFID) FROM #Temp_Lists WHERE DealFlg = 0 AND REFID > @REFID
30
31 END

改写方法2

declare @msg varchar(1000)
declare @refuseno varchar(30)
while 1=1
begin
select top 1 @refuseno = refuseno from #user_data
if @@rowcount = 0 break
/*
业务处理代码
*/
if @@error <> 0
begin
set @msg = '业务单转失败' + @refuseno
rollback
raiserror(@msg,16,1)
return
end
delete from #user_data where refuseno = @refuseno
end drop table #user_data

  

经过这样对原存储过程进行修正后,批量操作速度得到显著提升。

  有兴趣的朋友,可以尝试使用这个方法替代游标,对比2种方案的处理效率。

SQL Server 中用While循环替代游标Cursor的解决方案的更多相关文章

  1. [转]SQL Server中用While循环替代游标(Cursor)的解决方案

    本文转自:https://www.cnblogs.com/SunnyZhu/p/5719184.html By行处理数据,推荐2种方式: 1.游标 2.While循环 我们来了解下这两种方案处理1w行 ...

  2. SQL Server中用While循环替代游标(Cursor)的解决方案

    By行处理数据,推荐2种方式: 1.游标 2.While循环 我们来了解下这两种方案处理1w行数据分别需要多长时间. 一.游标. 首先我们填充一个表,用优雅的递归方式填充. ,) ) ;with ct ...

  3. SQL Server 内存泄露(memory leak)——游标导致的内存问题

    原文:SQL Server 内存泄露(memory leak)--游标导致的内存问题 转自:http://blogs.msdn.com/b/apgcdsd/archive/2011/07/01/sql ...

  4. 在SQL Server中用好模糊查询指令LIKE

    简介:like在sql中的使用 在SQL Server中用好模糊查询指令LIKE 查询是SQL Server中重要的功能,而在查询中将Like用上,可以搜索到一些意想不到的结果和效果,like的神奇之 ...

  5. 在SQL Server中用好模糊查询指令LIKE (转载)

    like在sql中的使用:在SQL Server中用好模糊查询指令LIKE:查询是SQL Server中重要的功能,而在查询中将Like用上,可以搜索到一些意想不到的结果和效果,like的神奇 一.一 ...

  6. SQL Server阻止了对组件xp_cmdshell过程的解决方案

    使用SQL tools连接sqlserver时候出现以下问题: SQL Server阻止了对组件xp_cmdshell过程的解决方案错误描述:SQL Server阻止了对组件‘xp_cmdshell’ ...

  7. SQL Server阻止了对组件xp_cmdshell过程的解决方案 分类: SQL Server 2015-03-05 08:31 305人阅读 评论(0) 收藏

    SQL Server阻止了对组件xp_cmdshell过程的解决方案 错误描述:SQL Server阻止了对组件'xp_cmdshell'的过程'sys.xp_cmdshell'的访问.因为此组件已作 ...

  8. SQL Server 事务、异常和游标

    转自:http://www.cnblogs.com/hoojo/archive/2011/07/19/2110325.html Ø 事务 在数据库中有时候需要把多个步骤的指令当作一个整体来运行,这个整 ...

  9. SQL Server 存储过程、触发器、游标

    存储过程 1.存储过程是事先编好的.存储在数据库中的程序,这些程序用来完成对数据库的指定操作. 2.系统存储过程: SQL Server本身提供了一些存储过程,用于管理有关数据库和用户的信息. 用户存 ...

随机推荐

  1. 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_09 序列化流_5_InvalidClassException异常_原理

    序列化一遍 反序列化再来一遍 age的修饰符改为public 直接反序列化就会抛出异常 序列化的时候会给Person.class添加序列号,serialVersionUID,.反序列化需要对比这个se ...

  2. Bootstrap 学习笔记 项目实战 响应式导航栏

    导航代码HTML: <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset=&q ...

  3. 应用安全 - 代码审计 -Java

    Java %c0%ae 安全模式绕过漏洞 原理 在Java端"%c0%ae"解析为"\uC0AE",最后转义为ASCCII低字符-".".通 ...

  4. ES6新增特性

    ES6:  ECMA 第六次改版   块级作用域:   凡是被{ }包裹住的代码都是块级作用域,除了对象       特点:会造成一个暂时性死区    新增声明变量的两种方式: let:     a. ...

  5. Robot Framework课件汇总

    http://www.testclass.net/rf/ 测试教程网http://www.testclass.net/all

  6. 浅谈WebService开发(一)转

    一.什么是WebService: 简单通俗来说,就是企业之间.网站之间通过Internet来访问并使用在线服务,一些数据,由于安全性问题,不能提供数据库给其他单位使用,这时候可以使   用WebSer ...

  7. expdp和impdp的应用-高版本通过dblink导入到低版本

    今天接到要进行数据库用户的部分数据迁移需求,需求如下 IMP.WO.INSA开头的表只要结构,不要数据 B.TEMP.TMP开头的表不用导 其他表需要导出数据和表结构,同时要求导出此用户下的所有其他对 ...

  8. 修改url,

    第一种场景: 无论url怎么变,表单里面的url始终不变 http://127.0.0.1:8000/CC/indexssssssssssssssssss/ url(r'^indexsssssssss ...

  9. Untiy3D的游戏物体的实例和刚体的使用

    一,如下代码, GameObject b = GameObject.Instantiate(bullet, transform.position, transform.rotation) as Gam ...

  10. python学习笔记(13):python并发编程以及系统常用模块

    一.进程与线程 1.进程:程序的一次执行(程序装载入内存,系统分配资源运行).n 每个进程有自己的内存空间.数据栈等,只能使用进程间通讯,而不能直接共享信息 2.线程:所有线程运行在同一个进程中,共享 ...