SQL Server 开窗函数 Over()代替游标的使用详解

这篇文章主要介绍了SQL Server 开窗函数 Over()代替游标的使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

前言:

今天在优化工作中遇到的sql慢的问题,发现以前用了挺多游标来处理数据,这样就导致在数据量多的情况下,需要一行一行去遍历从而计算需要的数据,这样处理的结果就是数据慢,容易卡死。

语法介绍:

1、与Row_Number() 函数结合使用,对结果进行排序,这个是我们使用的非常多的

SQL Server 开窗函数 Over()代替游标的使用详解

2、与聚合函数结合使用,利用over子句的分组和排序,对需要的数据进行操作

例如:SUM() Over() 累加值、AVG() Over() 平均数

MAX() Over() 最大值、MIN() Over() 最小值

具体介绍:

下面模拟工作中通过开窗函数代替游标的例子,通过期初余额与单据的预收金额、应收金额、实收金额来计算截止本单的期末余额,在以往就是通过游标一行一行去遍历,计算需要的期末余额,现在使用SUM() Over()来代替,最终要实现的效果图如下:

SQL Server 开窗函数 Over()代替游标的使用详解

第一行表示标题;第二行表示客户,是一行空行;第三行是期初余额,只显示期末余额的数据,第四至第六行表示的是每种单据的余额情况,并逐步汇总当前行的期末余额数据;最后一行表示的是对客户的合计。

1、构建需要用到的表和数据(简略版)

–客户表

CREATE TABLE Organization(

FItemID INT NOT NULL PRIMARY KEY IDENTITY(1,1),

FNumber NVARCHAR(255),

FName NVARCHAR(255)

)

–期初数据表

CREATE TABLE InitialData(

FID INT NOT NULL PRIMARY KEY IDENTITY(1,1),

FCustId INT NOT NULL,

FPreAmount DECIMAL(28,10) NOT NULL DEFAULT(0), –预收金额

FReceivableAmount DECIMAL(28,10) NOT NULL DEFAULT(0), –应收金额

FReceiveAmount DECIMAL(28,10) NOT NULL DEFAULT(0) –实收金额

)

–单据明细表

CREATE TABLE DetailData(

FID INT NOT NULL PRIMARY KEY IDENTITY(1,1),

FCustId INT NOT NULL,

FDate DATETIME NOT NULL,

FBillType NVARCHAR(64) NOT NULL,

FBillNo NVARCHAR(64) NOT NULL,

FPreAmount DECIMAL(28,10) NOT NULL DEFAULT(0), –预收金额

FReceivableAmount DECIMAL(28,10) NOT NULL DEFAULT(0), –应收金额

FReceiveAmount DECIMAL(28,10) NOT NULL DEFAULT(0) –实收金额

)

INSERT INTO Organization(FNumber,FName) VALUES(‘001′,’北京客户’)

INSERT INTO Organization(FNumber,FName) VALUES(‘002′,’上海客户’)

INSERT INTO Organization(FNumber,FName) VALUES(‘003′,’广州客户’)

INSERT INTO InitialData(FCustId,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(1,0,0,0)

INSERT INTO InitialData(FCustId,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(2,8000,7245,0)

INSERT INTO InitialData(FCustId,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(3,0,1068.21,1068.00)

INSERT INTO DetailData(FCustId,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(1,’2020-06-30′,’委托结算’,’XSD20200700008′,0,1221.56,0)

INSERT INTO DetailData(FCustId,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(1,’2020-06-30′,’委托结算’,’XSD20200700009′,0,373.46,0)

INSERT INTO DetailData(FCustId,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(1,’2020-06-30′,’委托结算退货’,’XSD20200700010′,0,-427.05,0)

INSERT INTO DetailData(FCustId,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(1,’2020-07-30′,’销售商品返利’,’XSFL20200700005′,0,-17.9,0)

INSERT INTO DetailData(FCustId,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(2,’2020-06-25′,’预收退款’,’SKD20200700002′,-755,0,0)

INSERT INTO DetailData(FCustId,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(2,’2020-06-20′,’销售发货’,’XSD20200700006′,0,6169.50,6169.50)

INSERT INTO DetailData(FCustId,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(2,’2020-07-30′,’销售总额返利’,’XSFL20200700002′,0,-493.56,-421.85)

INSERT INTO DetailData(FCustId,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(2,’2020-07-31′,’其他应收’,’QTYS20200900001′,0,6000.00,0)

INSERT INTO DetailData(FCustId,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(2,’2020-06-20′,’预收冲应收’,’HXD20200700006′,-7245.00,0,7245.00)

INSERT INTO DetailData(FCustId,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(3,’2020-06-30′,’销售收款’,’SKD20200700003′,0,0,2386.96)

INSERT INTO DetailData(FCustId,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(3,’2020-06-30′,’应收转应收’,’HXD20200700007′,0,2386.75,0)

INSERT INTO DetailData(FCustId,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount)

VALUES(3,’2020-07-08′,’销售退货’,’XSD20200700014′,0,-46.80,0)

GO

2、以往的游标写法

SET NOCOUNT ON

–建立临时表处理获取数据

CREATE TABLE #DATA(

FID INT NOT NULL PRIMARY KEY IDENTITY(1,1),

FClassTypeId INT NOT NULL,

FCustId INT NOT NULL,

FNumber NVARCHAR(255),

FName NVARCHAR(255),

FDate DATETIME NULL,

FBillType NVARCHAR(64) NULL,

FBillNo NVARCHAR(64) NULL,

FPreAmount DECIMAL(28,10) NOT NULL DEFAULT(0), –预收金额

FReceivableAmount DECIMAL(28,10) NOT NULL DEFAULT(0), –应收金额

FReceiveAmount DECIMAL(28,10) NOT NULL DEFAULT(0), –实收金额

FBalanceAmount DECIMAL(28,10) NOT NULL DEFAULT(0) –期末余额

)

Declare @Id INT

Declare @CustId INT

Declare @PreAmount decimal(28,10)

Declare @ReceivableAmount decimal(28,10)

Declare @ReceiveAmount decimal(28,10)

Declare @OldCustId int

Declare @Count int

Declare @LastAmount decimal(28,10)

Declare @SumPreAmount decimal(28,10)

Declare @SumReceivableAmount decimal(28,10)

Declare @SumReceiveAmount decimal(28,10)

Declare @SumBalanceAmount decimal(28,10)

–使用游标

Declare Data_cursor Cursor

For Select FID,FCustId,FPreAmount,FReceivableAmount,FReceiveAmount

From DetailData

Order By FCustId,FDate,FID

OPEN Data_cursor

FETCH NEXT FROM Data_Cursor INTO @Id,@CustId,@PreAmount,@ReceivableAmount,@ReceiveAmount

SET @OldCustId = @CustId

SET @Count = 0

SET @LastAmount = 0

SET @SumPreAmount = 0

SET @SumReceivableAmount = 0

SET @SumReceiveAmount = 0

SET @SumBalanceAmount = 0

WHILE @@FETCH_STATUS = 0

BEGIN

IF @Count > 0

BEGIN

IF @OldCustId @CustId

BEGIN

–表示客户已经变了,要插入小计

SET @Count = 0

INSERT INTO #DATA(FClassTypeId,FBillType,FCustId,FNumber,FName,FPreAmount,FReceivableAmount,FReceiveAmount,FBalanceAmount)

SELECT -9999,FName + ‘小计’,FItemID,FNumber,FName,@SumPreAmount,@SumReceivableAmount,@SumReceiveAmount,@LastAmount

FROM Organization

WHERE FItemID = @OldCustId

Select @SumPreAmount=0,@SumReceivableAmount=0,@SumReceiveAmount=0,@SumBalanceAmount=0,@LastAmount=0

END

END

IF @Count = 0

BEGIN

Set @OldCustId=@CustId

–插入一行空行

INSERT INTO #DATA(FClassTypeId,FBillType,FCustId,FNumber,FName)

SELECT -1000,FName,FItemID,FNumber,FName

FROM Organization

WHERE FItemID = @CustId

–获取期初的期末余额

SELECT @LastAmount=isnull(FReceivableAmount,0) – isnull(FPreAmount,0) – isnull(FReceiveAmount,0),@PreAmount=isnull(FPreAmount,0),@ReceivableAmount=isnull(FReceivableAmount,0),@ReceiveAmount=isnull(FReceiveAmount,0)

FROM InitialData

WHERE FCustId = @CustId

INSERT INTO #DATA(FClassTypeId,FBillType,FCustId,FNumber,FName,FBalanceAmount)

VALUES(-1000,’期初余额’,@CustId,”,”,@LastAmount)

SELECT @Count = 1

SELECT @SumBalanceAmount = @LastAmount

END

–插入单据明细

INSERT INTO #DATA(FClassTypeId,FCustId,FNumber,FName,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount,FBalanceAmount)

SELECT 0,d.FCustId,o.FNumber,o.FName,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount,@LastAmount + FReceivableAmount – FPreAmount – FReceiveAmount

FROM DetailData d

INNER JOIN Organization o ON d.FCustId = o.FItemID

WHERE d.FCustId = @CustId AND FID = @Id

SELECT

@LastAmount = @LastAmount + FReceivableAmount – FPreAmount – FReceiveAmount,

@SumPreAmount=@SumPreAmount + FPreAmount,@SumReceivableAmount=@SumReceivableAmount + FReceivableAmount,

@SumReceiveAmount=@SumReceiveAmount + FReceiveAmount

FROM DetailData

WHERE FCustId = @CustId AND FID = @Id

FETCH NEXT FROM Data_cursor INTO @Id,@CustId,@PreAmount,@ReceivableAmount,@ReceiveAmount

END

IF @Count > 0

BEGIN

INSERT INTO #DATA(FClassTypeId,FBillType,FCustId,FNumber,FName,FPreAmount,FReceivableAmount,FReceiveAmount,FBalanceAmount)

SELECT -9999,FName + ‘小计’,FItemID,FNumber,FName,@SumPreAmount,@SumReceivableAmount,@SumReceiveAmount,@LastAmount

FROM Organization

WHERE FItemID = @OldCustId

Select @SumPreAmount=0,@SumReceivableAmount=0,@SumReceiveAmount=0,@SumBalanceAmount=0,@LastAmount=0

END

CLOSE Data_cursor

DEALLOCATE Data_cursor

SELECT * FROM #DATA

ORDER BY FCustId,FID

DROP TABLE #DATA

代码说明:创建了一个临时表,使用游标遍历我们的DetailData数据表,为了呈现我们最终需要的数据样式,插入客户空行、期初余额、单据信息、客户小计等,逐行计算期末余额值的情况,最终效果如下:

SQL Server 开窗函数 Over()代替游标的使用详解

3、使用SUM() Over()的写法

SET NOCOUNT ON

–建立临时表处理获取数据

CREATE TABLE #DATA(

FID INT NOT NULL PRIMARY KEY IDENTITY(1,1),

FClassTypeId INT NOT NULL,

FCustId INT NOT NULL,

FNumber NVARCHAR(255),

FName NVARCHAR(255),

FDate DATETIME NULL,

FBillType NVARCHAR(64) NULL,

FBillNo NVARCHAR(64) NULL,

FPreAmount DECIMAL(28,10) NOT NULL DEFAULT(0), –预收金额

FReceivableAmount DECIMAL(28,10) NOT NULL DEFAULT(0), –应收金额

FReceiveAmount DECIMAL(28,10) NOT NULL DEFAULT(0), –实收金额

FBalanceAmount DECIMAL(28,10) NOT NULL DEFAULT(0) –期末余额

)

–插入空行

INSERT INTO #DATA(FClassTypeId,FBillType,FCustId,FNumber,FName)

SELECT -1000,FName,FItemID,FNumber,FName

FROM Organization o

INNER JOIN (SELECT FCustId FROM DetailData GROUP BY FCustId) d ON d.FCustId = o.FItemID

–插入期初余额

INSERT INTO #DATA(FClassTypeId,FBillType,FCustId,FNumber,FName,FBalanceAmount)

SELECT -1000,’期初余额’,FItemID,”,”,i.FReceivableAmount – i.FPreAmount -i.FReceiveAmount

FROM Organization o

INNER JOIN InitialData i ON o.FItemID = i.FCustId

INNER JOIN (SELECT FCustId FROM DetailData GROUP BY FCustId) d ON d.FCustId = o.FItemID

–插入单据明细(关键代码SUM() Over() )

INSERT INTO #DATA(FClassTypeId,FCustId,FNumber,FName,FDate,FBillType,FBillNo,FPreAmount,FReceivableAmount,FReceiveAmount,FBalanceAmount)

SELECT 0,d.FCustId,o.FNumber,o.FName,d.FDate,d.FBillType,d.FBillNo,d.FPreAmount,d.FReceivableAmount,d.FReceiveAmount,

SUM(d.FReceivableAmount – d.FPreAmount – d.FReceiveAmount) OVER(PARTITION BY d.FCustId ORDER BY d.FCustId,d.FDate,d.FID)

+ i.FReceivableAmount – i.FPreAmount – i.FReceiveAmount

FROM DetailData d WITH(NOLOCK)

INNER JOIN Organization o WITH(NOLOCK) ON o.FItemID = d.FCustId

INNER JOIN InitialData i WITH(NOLOCK) ON o.FItemID = i.FCustId

ORDER BY d.FCustId,d.FDate,d.FID

–插入小计

INSERT INTO #DATA(FClassTypeId,FBillType,FCustId,FNumber,FName,FPreAmount,FReceivableAmount,FReceiveAmount,FBalanceAmount)

SELECT -9999,FName + ‘小计’,d.FCustId,FNumber,FName,SUM(FPreAmount),SUM(FReceivableAmount),SUM(FReceiveAmount),0

FROM dbo.DetailData d

INNER JOIN dbo.Organization o ON d.FCustId = o.FItemID

GROUP BY d.FCustId,o.FName,o.FNumber

–更新小计的期末余额

UPDATE d SET d.FBalanceAmount = d.FReceivableAmount – d.FPreAmount – d.FReceiveAmount + i.FReceivableAmount – i.FPreAmount – i.FReceiveAmount

FROM #DATA d

INNER JOIN InitialData i ON d.FCustId = i.FCustId

WHERE d.FClassTypeId = -9999

SELECT * FROM #DATA

ORDER BY FCustId,FID

DROP TABLE #DATA

代码说明:相比第二种,去除了游标的写法,通过了

SUM(d.FReceivableAmount – d.FPreAmount – d.FReceiveAmount) OVER(PARTITION BY d.FCustId ORDER BY d.FCustId,d.FDate,d.FID)

来计算我们需要的值,这个语法说明一下,sum是累加计算,计算应收金额 – 预收金额 – 实收金额(第二行计算出来的结果要加上第一行计算出来的结果,第三行计算出来的结果要加上第二行计算出来的结果,依次类推,所以,其他聚合函数也是这种用法哦),PARTITION BY分组统计客户,并通过Order by指定排序

这个PARTITION BY和Order By结果的用法就很关键了,不然计算就不是预期想要的

再举个例子:比如使用Count() Over() 计算客户的订单号

SELECT DISTINCT FCustId,COUNT(FBillNo) OVER(PARTITION BY FCustId) FBillNum FROM DetailData

总结:

1、游标的使用场景可以很广,但是在数据量大的时候,就会显得很慢,一行一行遍历的速度还是挺久的

2、使用开窗函数来实现一些功能,还是很方便能实现效果,并且它的速度也是很快,值得推荐。

到此这篇关于SQL Server 开窗函数 Over()代替游标的使用的文章就介绍到这了,更多相关SQL Server 开窗函数 Over()内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

来源:脚本之家

链接:https://www.jb51.net/article/197562.htm

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:SEO优化专员,转转请注明出处:https://www.chuangxiangniao.com/p/895926.html

(0)
上一篇 2025年1月4日 00:28:19
下一篇 2025年1月3日 23:28:56

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 详解partition by和group by对比

    这篇文章主要介绍了详解partition by和group by对比,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 今天大概弄懂了partition by和group …

    2025年1月4日 数据库
    100
  • MariaDB数据库的外键约束实例详解

    约束保证了数据的完整性和一致性。下面这篇文章主要给大家介绍了关于MariaDB数据库的外键约束的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧 外键 外键的用途是确保数据的完整性。它通常包括以…

    数据库 2025年1月4日
    100
  • Access数据库日常维护方法(优化)

    这篇文章主要介绍了Access数据库日常维护方法(优化),需要的朋友可以参考下 本文适用范围:使用Access作为数据库建设的网站。 Access数据库的安全性 1、确保Access数据库不能直接下载,也就是通过http方式无法直接下载您的…

    数据库 2025年1月4日
    100
  • @@IDENTITY与SCOPE_IDENTITY()

    在一条 INSERT、SELECT INTO 或大容量复制语句完成后,@@IDENTITY 中包含语句生成的最后一个标识值 在一条 INSERT、SELECT INTO 或大容量复制语句完成后,@@IDENTITY 中包含语句生成的最后一个…

    数据库 2025年1月4日
    100
  • 解决sql server保存对象字符串转换成uniqueidentifier失败的问题

    这篇文章主要介绍了解决sql server保存对象字符串转换成uniqueidentifier失败的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧 一、 背景介绍 web应用采用的是ssh框架,数据库使用的sql se…

    数据库 2025年1月4日
    100
  • 阿里云如何设置帝国备份王后台数据库

    帝国备份王升级到了5.1版本,相比于以前的版本,设置相对复杂一些,如果你使用的是阿里云空间,且使用的是帝国备份王5.1以上版本,首次设置可能需要耗费一些时间,我尝试了五分钟左右,终于成功正确的设置了帝国备份王后台的各项参数,提供一下方法,节…

    数据库 2025年1月4日
    100
  • 图书管理系统的sqlserver数据库设计示例

    这篇文章主要介绍了图书管理系统的sqlserver数据库设计示例,文中通过E_R图、数据字典、数据库脚本代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 首先,在写数据库时需要遵循以下…

    2025年1月4日
    100
  • SQL Server中的集合运算: UNION, EXCEPT和INTERSECT示例代码详解

    这篇文章主要介绍了SQL Server中的集合运算: UNION, EXCEPT和INTERSECT,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下 SQL Server中的集合运算包括…

    2025年1月4日
    100
  • SQL Server异常代码处理的深入讲解

    这篇文章主要给大家介绍了关于SQL Server异常代码处理的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧 前言 SQL Server使用TRY…CATC…

    2025年1月4日
    100
  • sqlserver交集差集的用法详解

    这篇文章主要介绍了sql server 交集,差集的用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 概述 为什么使用集合运算: 在集合运算中比联接查询和EXIS…

    2025年1月4日 数据库
    100

发表回复

登录后才能评论

联系我们

156-6553-5169

在线咨询: QQ交谈

邮件:253000106@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

联系微信