SQL Server 中用While循环替代游标(Cursor的解

系统 2228 0

  在编写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代码如下:

While 循环
        
           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种方案的处理效率。

 

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


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论