概述

目前企业应用系统使用的大多数据库都是关系型数据库,关系数据库依赖的理论就是针对集合运算的关系代数。关系代数是一种抽象的查询语言,是关系数据操纵语言的一种传统表达方式。不过我们在工作中发现,很多人在面对复杂的数据库运算逻辑时会采用游标、循环、自定义函数等方式处理,因为游标是一种比较熟悉和舒适的面向过程的编程方式,很符合我们一般的逻辑思维习惯,可很不幸,这会导致糟糕的性能。显然,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集合运算参考及案例(一):列值分组累计求和的更多相关文章

  1. SQL集合运算参考及案例(二):树形节点数量逐级累计汇总

    问题描述: 我们经常遇到这样一个问题,类似于面对一个树形结构的物料数据,需要将库存中每一种物料数量汇总到物料上展示出来:或者说组织机构是一棵树,我们需要统计每一个节点上的人员数量(含下级节点的累计数量 ...

  2. SQL将一个表中的某一列值全部插入到另一个表中

    1.  SQL将一个表中的某一列值全部插入到另一个表中 插入的话: insert into a(col) select col from b; 更新的话: update a set col=selec ...

  3. 详解SQL集合运算

    以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化. 本系列[T-SQL基础]主要是针对T-SQL基础的总结. [T-SQL基础]01.单表查询-几道sql查询题 ...

  4. 7 SQL 集合运算

    7 集合运算 7-1 表的加减法 本章将会和大家一起学习“集合运算”操作.在数学领域,“集合”表示“(各种各样的)事物的总和”:在数据库领域,表示“记录的集合”.具体来说,表.视图和查询的执行结果都是 ...

  5. SQL集合运算 差集 并集 交

    SQL-3标准中提供了三种对检索结果进行集合运算的命令:并集UNION:交集INTERSECT:差集EXCEPT(在Oracle中叫做 MINUS).在有些数据库中对此的支持不够充分,如MySql中只 ...

  6. mybatis关联集合List&分布查询传递多列值

    场景:查询部门的同时,要求查询此部门下的所有用户. 部门(Department) private Integer id; private String departmentName; private ...

  7. sql 集合运算

    UNION 并运算 UNION 操作符用于合并两个或多个 SELECT 语句的结果集. 请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列.列也必须拥有相似的数据类型.同时,每条 SE ...

  8. SQL集合运算:差集、交集、并集

    1.差集( except ) select a from t_a except select a from t_b -- 也可写作: select a from t_a where a not in ...

  9. SQL集合运算

    注:UserInfo一共29条记录 select * from UserInfo union --并集(29条记录)(相同的只出现一次) select * from UserInfo select * ...

随机推荐

  1. 插入并列div使其居中

    <!doctype html><html> <head> <meta charset="UTF-8"> <meta name= ...

  2. magento additional & details 分解开来

    <?php foreach ($this->getChildGroup('detailed_info', 'getChildHtml') as $alias => $html):?& ...

  3. 第二个Sprint冲刺第一天

    讨论地点:宿舍 讨论成员:邵家文.李新.朱浩龙.陈俊金 任务:第二次sprint前期把选择题的计时功能完成. 燃尽图: 开发感悟: 大三第一学期就快结束了,大家都为未来找工作而烦恼.班里的高手都去学校 ...

  4. JavaScript中指针和地址理解

    个人理解:指针只是指向内存的一个索引:而地址则是内存中确切的位置. 下面是函数中关于指针和地址一个小例子: function sum(num1,num2){ return num1+num2; } a ...

  5. js基础知识(pomelo阅读)

    0,node.js调试: http://www.noanylove.com/2011/12/node-the-inspector-debugging-node-js/   1,读取配置文件: var ...

  6. LINQ 按多个字段排序

    多字段排序 添加到 LINQ 查询结果中的Take()扩展方法用于提取前 个结果: private static void Ordering() { var racers = (from r in F ...

  7. ZOJ 3804--解题报告

    题目相关: 3804相关链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5336 宠物(minion)在N*M的矩形玩游戏 ...

  8. 优测优社区干货精选|老司机乱谈编辑器之神——vim

    文 / 腾讯 吴双 前言 优测小优 有话说: 腾讯优测只有应用测试大神?不不不,我们还有各种研发大牛! *** vim 是一种信仰,我自从2004年有了这个信仰,已经12个年头了.本文介绍了学习vim ...

  9. 织梦dedecms模板调用标签大全-提高制作模板速度

    关键描述调用标签: ——————————————————————————–模板路径调用标签: {dede:field name=’templeturl’/}—————————————————————— ...

  10. Mysql 下 Insert、Update、Delete、Order By、Group By注入

    Insert: 语法:INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....) 报错注入: insert into test(id,name,p ...