博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用mssql2008新特性(存储过程参数类型使用"用户自定义表"来实现批量DML更新多表)解决项目里遇到的性能问题...
阅读量:6859 次
发布时间:2019-06-26

本文共 8068 字,大约阅读时间需要 26 分钟。

使用MSSQL2008有一段时间了,因为平常参与项目开发一般都使用NHbernate、Entity Framework等这些ORM工具来做数据的存储,所以并没有花时间去学习mssql2008,还是用mssql2000那会儿积累的经验来使MSSQL2008。当然也是粗略的了解到MSSQL2008也有表分区啊、自定义数据类型等。代码重构前一次操作需要数分钟,甚至半小时的操作重构后只需数秒至十几秒,前端经常卡死Ajax调用后台方法,后台不返回执行结果,后台出现超时错误等。

找到代码重构需要解决的问题:

1、原有代码实现方式是Entity Framework和拼接SQL混合的方式、因为需要同时更新多个表,Entity Framework实现起来是异常的吃力啊,前同事喜欢拼接大段的TSQL脚本在代码里实现,我的直觉是自己熟悉数据库,第一个想的就是使用存储过程来优化。见下图写法:

 

看到的问题就是很混乱,没有事务控制,现在系统就是经常出现异常时有些数据提交了有些没提交,部分进了第一个表,第二个表里状态没更新,你懂的,如此如此那般那般,我得经常维护这个项目,一直都有事情做。呵呵...

2、解决前台页面传递过来的JSON里处理多条记录,需要使用循环并嵌套循环的问题,提高效率。

解决的办法:

昨天看到知识库里的博文:“”,最后部分的使用MSSQL2008表参数来达到批量高效插入记录,转念一箱如果使用表参数一次传入需要循环多次传递的参数不是提高了速度吗?测试下来果真OK,效率提升了不止一点点啊,数十倍的提升!

C#调存储过程:

#region 准备DataTable            DataTable dt = new DataTable();            dt.Columns.Add(new DataColumn("RequestKeyID", typeof(Guid)));            dt.Columns.Add(new DataColumn("EntityCode", typeof(string)));            dt.Columns.Add(new DataColumn("Thirdparty", typeof(string)));            dt.Columns.Add(new DataColumn("PayStatus", typeof(int)));            dt.Columns.Add(new DataColumn("LoginUserName", typeof(string)));            foreach (JavaScriptObject r in rows)            {                year = int.Parse(r["Year"].ToString());                month = int.Parse(r["Month"].ToString());                var row = dt.NewRow();                row["RequestKeyID"] = Guid.NewGuid();                row["EntityCode"] = r["Entity"].ToString();                row["Thirdparty"] = r["ThirdPart"].ToString();                row["PayStatus"] = status;                row["LoginUserName"] = login;                dt.Rows.Add(row);            }            #endregion                       using (WHSEntities db = new WHSEntities())            {                db.AdoExecuteProc(WebConfigurationManager.ConnectionStrings["WHSEntities"].ConnectionString,                                  "Proc_StreamBatchPay",                                  new System.Data.SqlClient.SqlParameter[]                                  {                                      new System.Data.SqlClient.SqlParameter("@P_BatchRequestDataTable",SqlDbType.Structured){Value=dt},                                      new System.Data.SqlClient.SqlParameter("@PayYear",SqlDbType.Int){Value=year},                                      new System.Data.SqlClient.SqlParameter("@PayMonth",SqlDbType.Int){Value=month},                                      new System.Data.SqlClient.SqlParameter("@PayStatus",SqlDbType.Int){Value=status}                                  });            }

存储过程:

ALTER proc [dbo].[Proc_StreamBatchPay]             @P_BatchRequestDataTable DT_StreamBatchRequest READONLY , --临时表多个支付请求            @PayYear int ,  --支付的财务年            @PayMonth int,   --支付的财务月            @PayStatus int   --是支付1 还是拒绝0           /*  ***XXXXX过程  Date:  2013-3-14    原因:  1、批量插入和更新,加快页面反映速度,原来代码是EF和SQL混搭没有事务处理和页面反馈慢  2、使用事务来保证数据一致性  3、操作日志痕迹的保留*/         ASDECLARE @EntityCode  nvarchar(20) DECLARE @Thirdparty nvarchar(40)DECLARE @LoginUserName nvarchar(60) DECLARE @CountOfPaymentID intDECLARE @SerialID uniqueidentifierDECLARE @PaymentID uniqueidentifierBEGIN SET XACT_ABORT ON; SET NOCOUNT ON; BEGIN TRY    --开启事务    BEGIN TRANSACTION;        INSERT INTO dbo.FMS_WH_PaymentDetail                                                                    SELECT NEWID(),@PayStatus,p.PaymentID,GETDATE(),p.Accrual,p.UpdateBy,GETDATE(),p.ThirdPart,p.Category         FROM dbo.FMS_WH_Payment p              LEFT JOIN @P_BatchRequestDataTable i ON p.Entity = i.EntityCode and p.ThirdPart = i.Thirdparty        WHERE p.Year = @PayYear and  p.Month = @PayMonth and  p.Category = '3PLStream';                --支付             IF @PayStatus = 1            BEGIN               UPDATE dbo.FMS_WH_Payment SET UpdateBy= p.LoginUserName,UpdateDate=GETDATE(),Actual=Accrual               FROM @P_BatchRequestDataTable p               WHERE Entity = p.EntityCode  AND ThirdPart = p.Thirdparty                                                          AND Category = '3PLStream'  AND Year = @PayYear AND Month = @PayMonth;                                    SELECT p1.SerialID,p1.PaymentID,p1.OldYear,p1.OldMonth,p1.[Year],p1.[Month],p1.Entity,p1.ThirdPart               into #PayMent1Temp               FROM dbo.FMS_WH_Payment1 p1                WHERE p1.Year = @PayYear AND                     p1.Month = @PayMonth AND                     p1.Entity+'-'+p1.ThirdPart IN (SELECT E.EntityCode+'-'+E.Thirdparty FROM @P_BatchRequestDataTable E) AND                                          p1.Category = '3PLStream';                                    --游标处理PayMent1里对应的记录               DECLARE CUR_PAYMENT1 CURSOR FOR SELECT SerialID,PaymentID FROM #PayMent1Temp                OPEN CUR_PAYMENT1;               FETCH NEXT FROM CUR_PAYMENT1  INTO  @SerialID,@PaymentID;               WHILE @@FETCH_STATUS = 0                  BEGIN                     SELECT @CountOfPaymentID = COUNT(*) FROM FMS_WH_Payment1                      WHERE  PaymentID=@PaymentID;                     --没跨月,当月发生当月支付                     IF @CountOfPaymentID = 1                        BEGIN                         UPDATE FMS_WH_Payment1 SET Actual=Accrual                          WHERE SerialID = @SerialID;                       END                     --跨月,当月发生下月支付                     IF @CountOfPaymentID = 2                         BEGIN                            --更新发生月那笔payment1记录实际付款金额                            UPDATE FMS_WH_Payment1 SET Actual=Accrual                            WHERE SerialID = @SerialID;                            --删除结转的那笔用于显示的payment1记录                            DELETE FMS_WH_Payment1 WHERE PaymentID=@PaymentID AND OldMonth <> [Month];                        END                         --跨月,当月发生下下月支付 原则上不允许流量跨2月                     IF @CountOfPaymentID = 3                         BEGIN                            --更新发生月那笔payment1记录实际付款金额                            UPDATE FMS_WH_Payment1 SET Actual=Accrual                            WHERE PaymentID=@PaymentID AND OldMonth = [Month];                            --删除结转的那2笔用于显示的payment1记录                            DELETE FMS_WH_Payment1 WHERE PaymentID=@PaymentID AND OldMonth <> [Month];                        END                              FETCH NEXT FROM CUR_PAYMENT1  INTO  @SerialID,@PaymentID                           END               CLOSE CUR_PAYMENT1;               DEALLOCATE CUR_PAYMENT1;               --操作成功,写日志               INSERT INTO fms_system_log(dtDate,sThread,sLevel,sLogger,sMessage,sException)               SELECT GETDATE(),'DBLOG','INFO','dbProc','02|执行了'+CAST(@PayYear AS VARCHAR)+'\'+CAST(@PayMonth AS VARCHAR)+'批量流量费用支付操作。','';             END          --拒绝          IF @PayStatus = 0            BEGIN               UPDATE dbo.FMS_WH_Payment SET UpdateBy= p.LoginUserName,UpdateDate=GETDATE(),[status]=0               FROM @P_BatchRequestDataTable p               WHERE Entity = p.EntityCode AND ThirdPart = p.Thirdparty                                                            AND Category = '3PLStream'  AND Year = @PayYear  AND Month = @PayMonth;                --写日志               INSERT INTO fms_system_log(dtDate,sThread,sLevel,sLogger,sMessage,sException)               SELECT GETDATE(),'DBLOG','INFO','dbProc','02|执行了'+CAST(@PayYear AS VARCHAR)+'\'+CAST(@PayMonth AS VARCHAR)+'批量流量费用拒绝支付的操作。','';            END    --提交事务    COMMIT TRANSACTION; END TRY BEGIN CATCH        IF (XACT_STATE()) = -1    BEGIN        ROLLBACK TRANSACTION;        --异常日志记录        INSERT INTO fms_system_log(dtDate,sThread,sLevel,sLogger,                                   sMessage,sException)        SELECT GETDATE(),'DBLOG','ERROR','dbProc',               '异常代码:'+CAST(ERROR_NUMBER() AS VARCHAR)+'\异常消息:'+ERROR_MESSAGE(),CAST(ERROR_NUMBER() AS VARCHAR)    END;       IF (XACT_STATE()) = 1    BEGIN        COMMIT TRANSACTION;       END; END CATCH;END

这是特定业务写的存储过程没什么用处,也不需要看懂,就是看看表参数是怎么用的,怎么使用事务来优化控制数据的一致性。看存储过程里的注释即可。

别告诉哥EF怎么调整性能好,哥没那时间去学习,多年数据库编程经验哥自信在这个时刻ADO.NET对于我是最可靠的。

 

OK。搞定收工,主要是留下思路给自己以后回顾的,万一能帮到你也解决了类似问题,那我就更开心了!请不要说没有截图对比效率提升,无图无真相;是真不方便哦。没效果我就不会分享了。

 

转载地址:http://zbxyl.baihongyu.com/

你可能感兴趣的文章
List与String的相互转换
查看>>
换行符导致的脚本错误调试
查看>>
Android——Android Sutido:[2]导入eclipse项目篇
查看>>
setsockopt之 TCP_KEEPIDLE/TCP_KEEPINTVL/TCP_KEEPCNT
查看>>
typeid详解
查看>>
SQL Server中的Image数据类型的操作
查看>>
Atitit.html css 浏览器原理理论概论导论attilax总结
查看>>
求解圆圈中最后剩下的数字
查看>>
jQuery入门第二天
查看>>
boost中的智能指针
查看>>
Windows下Php安装mongodb扩展失败
查看>>
discuz安装步骤
查看>>
IntelliJ IDEA修改Output输出缓存区大小【应对:too much output to process】
查看>>
计算机网络概述
查看>>
(转) WTF is computer vision?
查看>>
html标签的target属性应用
查看>>
长连接
查看>>
MySQL数据库权限操作指南
查看>>
rabbitmq的web管理界面无法使用guest用户登录
查看>>
HBase的集群搭建(1、3、5节点都适用)
查看>>