SQL Server 游标的使用示例
Ø 简介
本文主要记录 MSSQL 中的游标使用示例,在有必要时方便借鉴查阅。游标一般定义在某段功能性的 SQL 语句中,或者存储过程中。之所以选择用它,是因为有时候无法使用简单的 SQL 语句满足我们需求,比如需要对结果集中的每一条数据,根据不同条件进行不同操作(CRUD),这时我们就可以使用游标来完成。
提示:来之 DBA 的杰作,哈哈~~
1. 示例1:
本示例,用于初始化某新表的数据。使用游标遍历查询结果集,根据遍历的数据再插入另外两张表。SQL 代码如下:
IF(OBJECT_ID('SP_Init_CustomerNewOpening', :
1) 本示例,创建了一个存储过程,创建了两个游标。
2) 第一个游标,使用游标完成更新遍历的记录,再使用遍历的数据完成对另外两张表的 Insert 操作。
3) 第二个游标,完成对3张表的更新操作,在示例中使用了当前游标记录作为更新条件,例如:WHERE CURRENT OF cursor_Crm_Refundment。
4) 该存储过程用于定时任务执行,比在程序中去查询数据,然后再遍历写入数据效率上还是比较有优势的,而且便于维护。SQL 代码如下:
IF(OBJECT_ID('SP_Add_CustomerNewOpening', 'P') IS NOT NULL)
DROP PROCEDURE SP_Add_CustomerNewOpening;
GO
CREATE PROCEDURE SP_Add_CustomerNewOpening
(
@StartTime datetime, --开始时间
@EndTime datetime --结束时间
)
AS
DECLARE @Remark varchar(8000) = NULL;
INSERT INTO Sys_DbLog(LogTime, Type, Item, Message, Remark) VALUES(GETDATE(), 'info', 'SP_Add_CustomerNewOpening', '存储过程执行开始', NULL);
DECLARE @RewardsMoney decimal(18,2) = CAST(ISNULL((SELECT Value FROM Config WHERE Id=79), '0') AS decimal(18,2));
DECLARE @NewOpenMoney decimal(18,2) = CAST(ISNULL((SELECT Value FROM Config WHERE Id=80), '0') AS decimal(18,2));
DECLARE @AddCustNewOpenCount int = 0, @AddOrderNewOpenCount int = 0, @UpdateCustNewOpenCount int = 0,
@UpdateOrderNewOpenCount int = 0, @UpdateRefundmentCount int = 0;
BEGIN TRY
BEGIN TRANSACTION;
--1. 新增新开客户
DECLARE @OrderNewOpenId int, @CustNewOpenId int;
DECLARE @UserId bigint, @PayTime datetime, @Address nvarchar(500), @CustomerId bigint, @EmplCityId int, @EmployeeId int, @RealTotal decimal(18,2);
DECLARE cursor_CustomerNewOpening CURSOR GLOBAL SCROLL DYNAMIC SCROLL_LOCKS
FOR SELECT UserId, PayTime, Address, CustomerId, EmplCityId, EmployeeId, SUM(RealTotal) AS RealTotal FROM
(
SELECT t1.UserId, t1.PayTime, t1.Address, t2.CustomerId, t4.CityId AS EmplCityId, t4.EmployeeId, t1.RealTotal FROM Orders AS t1
INNER JOIN UserInfo AS t2 ON(t1.UserId = t2.Id)
INNER JOIN Customer AS t3 ON(t2.CustomerId = t3.Id)
INNER JOIN Sys_EmployeeProfile AS t4 ON(t1.SalesUserId = t4.EmployeeId)
WHERE 1=1
AND t1.UserId > 0
AND t2.CustomerId > 0
AND t1.SalesUserId > 0
AND (t1.OrderStatusId > 1 AND t1.OrderStatusId < 10)
AND (t1.PayStatusId = 2 AND t1.PayTime >= @StartTime AND t1.PayTime <= @EndTime)
AND NOT EXISTS(SELECT TOP 1 1 FROM Crm_CustomerNewOpening AS T WHERE T.CustomerId = t2.CustomerId AND (T.NewOpenStatus = 1 OR T.NewOpenStatus = 4))
AND NOT EXISTS(SELECT TOP 1 1 FROM Crm_OrderNewOpening AS T WHERE T.OrderId = t1.Id)
) AS T WHERE 1=1
AND RealTotal >= @NewOpenMoney
GROUP BY UserId, PayTime, Address, CustomerId, EmplCityId, EmployeeId;
OPEN cursor_CustomerNewOpening;
FETCH FIRST FROM cursor_CustomerNewOpening INTO @UserId, @PayTime, @Address, @CustomerId, @EmplCityId, @EmployeeId, @RealTotal;
WHILE(@@FETCH_STATUS = 0)
BEGIN
--防止本次执行多条符合的情况
IF(NOT EXISTS(SELECT TOP 1 1 FROM Crm_CustomerNewOpening AS T WHERE T.CustomerId = @CustomerId AND (T.NewOpenStatus = 1 OR T.NewOpenStatus = 4)))
BEGIN
--插入客户新开
INSERT INTO Crm_CustomerNewOpening(CustomerId, EmplCityId, EmployeeId, RewardsMoney, NewOpenMoney, RealTotal,
RefundmentMoney, PayTime, NewOpenStatus, AuditorId, AuditorTime, AuditDesc, CreateTime, UpdateTime)
VALUES(@CustomerId, @EmplCityId, @EmployeeId, @RewardsMoney, @NewOpenMoney, @RealTotal,
0, @PayTime, 1/*当前有效*/, NULL, NULL, NULL, GETDATE(), NULL);
SELECT @CustNewOpenId = SCOPE_IDENTITY(), @AddCustNewOpenCount = (@AddCustNewOpenCount + @@ROWCOUNT);
--插入订单新开
INSERT INTO Crm_OrderNewOpening(CustNewOpenId, OrderId, OrderNum, RealTotal, RefundmentMoney, CreateTime, UpdateTime)
SELECT @CustNewOpenId, t1.Id, t1.OrderNum, t1.RealTotal, 0, GETDATE(), NULL FROM Orders AS t1
WHERE 1=1
AND t1.UserId = @UserId
AND t1.PayTime = @PayTime
AND t1.Address = @Address;
SELECT @AddOrderNewOpenCount = (@AddOrderNewOpenCount + @@ROWCOUNT);
END
FETCH NEXT FROM cursor_CustomerNewOpening INTO @UserId, @PayTime, @Address, @CustomerId, @EmplCityId, @EmployeeId, @RealTotal;
END
CLOSE cursor_CustomerNewOpening;
DEALLOCATE cursor_CustomerNewOpening;
--2. 更新客户新开(退款失效)
DECLARE @RefundmentId int, @OrderId bigint, @RefundmentMoney decimal(18,2);
DECLARE cursor_Crm_Refundment CURSOR GLOBAL SCROLL DYNAMIC SCROLL_LOCKS
FOR SELECT Id, OrderId, RefundmentMoney FROM Crm_Refundment AS t1 WHERE t1.OrderNewOpenId IS NULL
AND t1.CreateTime >= @StartTime AND t1.CreateTime <= @EndTime
FOR UPDATE OF OrderNewOpenId, UpdateTime;
OPEN cursor_Crm_Refundment;
FETCH FIRST FROM cursor_Crm_Refundment INTO @RefundmentId, @OrderId, @RefundmentMoney;
WHILE(@@FETCH_STATUS = 0)
BEGIN
SELECT @OrderNewOpenId = NULL, @CustNewOpenId = NULL;
SELECT @OrderNewOpenId = Id, @CustNewOpenId = CustNewOpenId FROM Crm_OrderNewOpening WHERE OrderId = @OrderId;
IF(@OrderNewOpenId IS NOT NULL AND @CustNewOpenId IS NOT NULL)
BEGIN
DECLARE @RowCount1 int = 0, @RowCount2 int = 0;
--1. 更新订单新开
UPDATE Crm_OrderNewOpening SET RefundmentMoney = (RefundmentMoney + @RefundmentMoney), UpdateTime = GETDATE()
WHERE Id = @OrderNewOpenId;
SELECT @RowCount1 = @@ROWCOUNT, @UpdateOrderNewOpenCount = (@UpdateOrderNewOpenCount + @@ROWCOUNT);
--2. 更新客户新开
UPDATE Crm_CustomerNewOpening SET RefundmentMoney = (RefundmentMoney + @RefundmentMoney),
NewOpenStatus = (CASE WHEN NewOpenStatus = 1 AND (RealTotal - (RefundmentMoney + @RefundmentMoney)) < @NewOpenMoney
THEN 2 ELSE NewOpenStatus END)/*只有【当前有效】状态才更新状态*/,
RefundmentId = (CASE WHEN NewOpenStatus = 1 AND (RealTotal - (RefundmentMoney + @RefundmentMoney)) < @NewOpenMoney
--AND RefundmentId IS NULL/*逻辑上可以省略该条件,因为。。。*/
THEN @RefundmentId ELSE RefundmentId END)/*记录导致退款失效的退款Id*/,
UpdateTime = GETDATE()
WHERE Id = @CustNewOpenId;
SELECT @RowCount2 = @@ROWCOUNT, @UpdateCustNewOpenCount = (@UpdateCustNewOpenCount + @@ROWCOUNT);
IF(@RowCount1 = 1 AND @RowCount2 = 1)
BEGIN
UPDATE Crm_Refundment SET OrderNewOpenId = @OrderNewOpenId, UpdateTime = GETDATE() WHERE CURRENT OF cursor_Crm_Refundment;
SELECT @UpdateRefundmentCount = (@UpdateRefundmentCount + @@ROWCOUNT);
END
END
FETCH NEXT FROM cursor_Crm_Refundment INTO @RefundmentId, @OrderId, @RefundmentMoney;
END
CLOSE cursor_Crm_Refundment;
DEALLOCATE cursor_Crm_Refundment;
COMMIT;
SET @Remark = '新开客户数:' + CAST(@AddCustNewOpenCount AS varchar(20))
+ ',新开订单数:' + CAST(@AddOrderNewOpenCount AS varchar(20))
+ ',更新新开客户数:' + CAST(@UpdateCustNewOpenCount AS varchar(20))
+ ',更新新开订单数:' + CAST(@UpdateOrderNewOpenCount AS varchar(20))
+ ',更新退款数:' + CAST(@UpdateRefundmentCount AS varchar(20));
INSERT INTO Sys_DbLog(LogTime, Type, Item, Message, Remark) VALUES(GETDATE(), 'info', 'SP_Add_CustomerNewOpening', '存储过程执行结束', @Remark);
END TRY
BEGIN CATCH
ROLLBACK;
DECLARE @Message varchar(8000) = '错误:[' + CAST(ERROR_LINE() AS nvarchar(20)) + '],' + ERROR_MESSAGE();
INSERT INTO Sys_DbLog(LogTime, Type, Item, Message, Remark) VALUES(GETDATE(), 'error', 'SP_Add_CustomerNewOpening', @Message, '事物已回滚');
END CATCH
GO
SQL Server 游标的使用示例的更多相关文章
- SQL Server游标 C# DataTable.Select() 筛选数据 什么是SQL游标? SQL Server数据类型转换方法 LinQ是什么? SQL Server 分页方法汇总
SQL Server游标 转载自:http://www.cnblogs.com/knowledgesea/p/3699851.html. 什么是游标 结果集,结果集就是select查询之后返回的所 ...
- SQL Server 游标运用:鼠标轨迹字符串分割
一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 背景(Contexts) 游标模板(Cursor Template) 鼠标轨迹字符串分割SQL脚本实现(SQL Code ...
- sql server 游标的简单用法
sql server游标: --定义游标 declare cursor1 cursor for select ID,Name from A --打开游标 open cursor1 declare @i ...
- SQL Server 游标运用:查看所有数据库所有表大小信息(Sizes of All Tables in All Database)
原文:SQL Server 游标运用:查看所有数据库所有表大小信息(Sizes of All Tables in All Database) 一.本文所涉及的内容(Contents) 本文所涉及的内容 ...
- SQL Server 游标运用:查看一个数据库所有表大小信息(Sizes of All Tables in a Database)
原文:SQL Server 游标运用:查看一个数据库所有表大小信息(Sizes of All Tables in a Database) 一.本文所涉及的内容(Contents) 本文所涉及的内容(C ...
- 学习使用MS SQL Server游标(CURSOR)
说实的,使用MS SQL Server这样久,游标一直没有使用过.以前实现相似的功能,都是使用WHILE循环加临时表来实现.刚才有参考网上示例练习写了一下.了解到游标概念与语法. 下面代码示例中,先是 ...
- Sql Server 游标概念与实例
引言 先不讲游标的什么概念,看如下Sql Server2008 图例: 需求:两张表的O_ID是一一对应的,现在求将加薪的工资+原来的工资=现在的工资,也就是O_Salary=O_Salary+A_S ...
- 2009-04-19 22:40 SQL SERVER游标的讲解
游标和游标的优点 在数据库中,游标是一个十分重要的概念.游标提供了一种对从表中检索出的数据进行操作的灵活手段,就本质而言,游标实际上是一种能从包括多条数据记录的结 果集中每次提取一条记录的机制.游标总 ...
- SQL Server游标(转)
清晰地介绍了SQL游标,很好的学习资料. 转自 http://www.cnblogs.com/knowledgesea/p/3699851.html 什么是游标 结果集,结果集就是select查询之后 ...
随机推荐
- [20190423]那个更快的疑问3.txt
[20190423]那个更快的疑问3.txt --//前一阵子,做了11g在单表单条记录唯一索引扫描的测试,摘要如下:--//参考链接:http://blog.itpub.net/267265/vie ...
- Linux Mint如何安装“微信、QQ、迅雷、WPS办公软件”等国内上瘾软件
很多小伙伴都用ubuntu或者Linux Mint,但由于已经习惯了让人成瘾的国产软件,比如迅雷,qq,微信等,其实我们应该培养更为健康的上网习惯,这些软件不是非用不可,但如果你不用不行, 那么也是有 ...
- Conway生命游戏
版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖.如要转贴,必须注明原文网址 http://www.cnblogs.com/Colin-Cai/p/9986679.html 作者:窗户 Q ...
- Socket与WebScoket
socket 英文socket的意思是插座,网络中的Socket是一个抽象的接口,可以理解为网络中连接的两端.通常被叫做套接字接口,其意义在对传输层进行封装屏蔽了传输层的复杂性.它并不是一个协议,是为 ...
- iOS 友盟错误分析-2019
友盟的错误分析越来越人性化了 前提集成了友盟统计,并打包的时候保留了.dSYM文件 先看看效果 可以看到bug显而易见的被发现了!那个文件夹,那一行代码 那么怎么才能这样呢 首先加入符号表,就是.dS ...
- JS 设计模式八 -- 发布订阅者模式
概念 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多(一个发布,多个观察)的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 优点 1 ...
- MYSQL 双主配置
MYSQL1. 版本号:5.7.243. 部署方式:双主部署,两台机器即是主又是备 ,双向拷贝,可以同时写入.4. 安装部署路径: a) /home/softb) 配置路径 /etc/my.cnfc) ...
- MTK刷机快捷键
彻底关机后按住音量下键,连接电脑 无法关机的情况下按住音量上+电源键,手机黑屏后松开电源键,连接电脑,出现进度条后松开音量上键
- Net包管理NuGet(4)打包nuget包的进阶用法
nuget包不止可以放进dll,还可以引用其他包,创建项目文件及替换配置文件 引用的时候同时引用依赖项假设引用某个dll需要依赖其他的dll如Zeta.Core.dll需要引用ZETA.Seriali ...
- pm2 常用命令解析
https://blog.csdn.net/chengxuyuanyonghu/article/details/74910875 (以上基本命令解析,一下补充) pm2 ecosystem #在当前 ...