SQL集合运算参考及案例(一):列值分组累计求和
概述
目前企业应用系统使用的大多数据库都是关系型数据库,关系数据库依赖的理论就是针对集合运算的关系代数。关系代数是一种抽象的查询语言,是关系数据操纵语言的一种传统表达方式。不过我们在工作中发现,很多人在面对复杂的数据库运算逻辑时会采用游标、循环、自定义函数等方式处理,因为游标是一种比较熟悉和舒适的面向过程的编程方式,很符合我们一般的逻辑思维习惯,可很不幸,这会导致糟糕的性能。显然,SQL的总体目的是你要实现什么,而不是怎样实现。大道至简,我们在工作与学习的过程中经常会发现,更好的解决方案往往是简单的,是高效的,是优雅的。
本人曾经用T-SQL重写了一个基于游标的存储过程,那个表只有100,000条记录,原来的存储过程用了40分钟才执行完毕,而新的存储过程只用了不到1秒。在这里,我想将自己遇到和收集到的关于集合运算与游标操作的对比展现给大家,以供参考。
问题描述
我们有时会遇到这样一个问题,类似于某一列的值累计求和(即本条记录的某个值=前几列该值的合计)。我将解决的核心部分抽取出来。
--- 原始数据如下:
OID |
Period |
Amount |
Balance |
1 |
2009 |
3500.00 |
0.00 |
2 |
2009 |
5100.00 |
0.00 |
3 |
2009 |
10000.00 |
0.00 |
4 |
2010 |
2560.00 |
0.00 |
5 |
2010 |
4700.00 |
0.00 |
-- 预期结果如下(求Balance的值):
OID |
Period |
Amount |
Balance |
1 |
2009 |
3500.00 |
3500.00 |
2 |
2009 |
5100.00 |
8600.00 |
3 |
2009 |
10000.00 |
18600.00 |
4 |
2010 |
2560.00 |
2560.00 |
5 |
2010 |
4700.00 |
7260.00 |
创建测试数据的SQL脚本
- CREATE TABLE tPeriod
- (
- OID INT IDENTITY PRIMARY KEY
- , Period NVARCHAR(20)
- , Amount DECIMAL(18, 2) DEFAULT 0
- , Balance DECIMAL(18, 2) DEFAULT 0
- , Balance2 DECIMAL(18, 2) DEFAULT 0
- , Balance3 DECIMAL(18, 2) DEFAULT 0
- )
- GO
- DECLARE @i INT
- SET @i = 1900
- WHILE @i <= 2013
- BEGIN
- INSERT INTO tPeriod(Period, Amount)
- SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- SET @i = @i + 1
- END
- INSERT INTO tPeriod(Period, Amount)
- SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- UNION ALL SELECT CAST(@i AS NVARCHAR), ROUND(RAND() * 10000, -2)
- GO
- SELECT * FROM tPeriod;
- GO
传统解答:使用游标
- DECLARE @OID INT
- , @vPeriod_Pre NVARCHAR(20)
- , @vPeriod_Current NVARCHAR(20)
- , @dcAmount DECIMAL(18, 2)
- , @dcBalance DECIMAL(18, 2)
- DECLARE cursor1 CURSOR FOR
- SELECT t.OID, t.Period, t.Amount from tPeriod AS t
- OPEN cursor1
- FETCH NEXT FROM cursor1 INTO @OID, @vPeriod_Current, @dcAmount
- SELECT @vPeriod_Pre = @vPeriod_Current, @dcBalance = 0
- WHILE @@FETCH_STATUS = 0
- BEGIN
- IF @vPeriod_Current = @vPeriod_Pre
- BEGIN
- SET @dcBalance = @dcBalance + @dcAmount
- END
- ELSE
- BEGIN
- SELECT @vPeriod_Pre = @vPeriod_Current, @dcBalance = @dcAmount
- END
- UPDATE tPeriod
- SET Balance = @dcBalance
- WHERE OID = @OID
- FETCH NEXT FROM cursor1 INTO @OID, @vPeriod_Current, @dcAmount
- END
- CLOSE cursor1
- DEALLOCATE cursor1
推荐解答:集合运算
- -- 参考答案2
- UPDATE tPeriod
- SET Balance3 = ( SELECT SUM(Amount)
- FROM tPeriod AS t
- WHERE t.Period = tPeriod.Period AND t.OID <= tPeriod.OID
- )
- GO
- -- 参考答案3(SQLSERVER)
- DECLARE @dcAmt DECIMAL(18, 2), @period CHAR(4)
- UPDATE T1
- SET @dcAmt = CASE WHEN Period = @period THEN @dcAmt + Amount ELSE Amount END,
- @Period = Period,
- Balance2 = @dcAmt
- FROM tPeriod AS T1
- GO
- -- 参考答案3(Oracle)
- SELECT t.*, sum(t.amount) over(partition BY t.Period order by t.OID) as acc
- FROM tPeriod t;
SQL集合运算参考及案例(一):列值分组累计求和的更多相关文章
- SQL集合运算参考及案例(二):树形节点数量逐级累计汇总
问题描述: 我们经常遇到这样一个问题,类似于面对一个树形结构的物料数据,需要将库存中每一种物料数量汇总到物料上展示出来:或者说组织机构是一棵树,我们需要统计每一个节点上的人员数量(含下级节点的累计数量 ...
- SQL将一个表中的某一列值全部插入到另一个表中
1. SQL将一个表中的某一列值全部插入到另一个表中 插入的话: insert into a(col) select col from b; 更新的话: update a set col=selec ...
- 详解SQL集合运算
以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化. 本系列[T-SQL基础]主要是针对T-SQL基础的总结. [T-SQL基础]01.单表查询-几道sql查询题 ...
- 7 SQL 集合运算
7 集合运算 7-1 表的加减法 本章将会和大家一起学习“集合运算”操作.在数学领域,“集合”表示“(各种各样的)事物的总和”:在数据库领域,表示“记录的集合”.具体来说,表.视图和查询的执行结果都是 ...
- SQL集合运算 差集 并集 交
SQL-3标准中提供了三种对检索结果进行集合运算的命令:并集UNION:交集INTERSECT:差集EXCEPT(在Oracle中叫做 MINUS).在有些数据库中对此的支持不够充分,如MySql中只 ...
- mybatis关联集合List&分布查询传递多列值
场景:查询部门的同时,要求查询此部门下的所有用户. 部门(Department) private Integer id; private String departmentName; private ...
- sql 集合运算
UNION 并运算 UNION 操作符用于合并两个或多个 SELECT 语句的结果集. 请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列.列也必须拥有相似的数据类型.同时,每条 SE ...
- SQL集合运算:差集、交集、并集
1.差集( except ) select a from t_a except select a from t_b -- 也可写作: select a from t_a where a not in ...
- SQL集合运算
注:UserInfo一共29条记录 select * from UserInfo union --并集(29条记录)(相同的只出现一次) select * from UserInfo select * ...
随机推荐
- Golang Deco Enco
mproto.go package mproto import ( "bytes" "encoding/binary" "fmt" &quo ...
- Windows Store App, Shaken
Accelerometer _accelerometer; ; public MainPage() { this.InitializeComponent(); _accelerometer=Accel ...
- highcharts 的使用实例:待写
http://www.hcharts.cn/demo/index.php 方法一:在Axis(包括xAxis和yAxis)有一个属性tickInterval,number类型,表示间隔,也就是间隔多少 ...
- Dynamic支持CollectionView布局 、 MotionEffects特效 、 BlurImage效果 、 TextKit
1 使用UIDynamicAnimator对集合视图进行布局 1.1 问题 UIKit Dynamic动力模型一个非常有趣的用途就是影响集合视图的布局,可以给集合视图的布局添加各种动力行为,使其产生丰 ...
- ASP.NET定制简单的错误处理页面
通常Web应用程序在发布后,为了给用户一个友好界面和使用体验,都会在错误发生时跳转至一个自定义的错误页面,而不是ASP.net向用户暴露出来的详细的异常列表. 简单的错误处理页面可以通过web.con ...
- 使用容器控制器控制另外两个控制器的view交换
建三个UIViewController 的子控制器,其中一个为根控制器,另外两个控制器的视图作为切换对象 AppDelegate中代码 //AppDelegate.h中代码 #import <U ...
- BUTTON标签和INPUT标签的区别【转】
一句话概括主题:<button>具有<input type="button" ... >相同的作用但是在可操控性方面更加强大. HTML 4.01规范的Fo ...
- LA 3644 易爆物 并查集
题目链接: https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show ...
- C#部分---特殊集合:stack栈集合、queue队列集合、哈希表集合。
1.stack栈集合:又名 干草堆集合 栈集合 特点:(1)一个一个赋值 一个一个取值(2)先进后出实例化 初始化 Stack st = new Stack(); //添加元素用push st.Pus ...
- scala言语基础学习十一
隐式转换 使用隐式转换加强现有的类型的功能-类似于设计模式的装饰模式