概述

目前企业应用系统使用的大多数据库都是关系型数据库,关系数据库依赖的理论就是针对集合运算的关系代数。关系代数是一种抽象的查询语言,是关系数据操纵语言的一种传统表达方式。不过我们在工作中发现,很多人在面对复杂的数据库运算逻辑时会采用游标、循环、自定义函数等方式处理,因为游标是一种比较熟悉和舒适的面向过程的编程方式,很符合我们一般的逻辑思维习惯,可很不幸,这会导致糟糕的性能。显然,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. ibatis 的 "This SQL map does not contain a MappedStatement"的错误

    This SQL map does not contain a MappedStatement named List 说明: 执行当前 Web 请求期间,出现未处理的异常.请检查堆栈跟踪信息,以了解有 ...

  2. 转载:C# Word操作实现代码

    转载自:http://www.jb51.net/article/17770.htm 在VS2008平台下,引用.net-Microsoft.Office.Interop.Word.12,这样就可以在程 ...

  3. IOS的MVC

    1 翻牌游戏 1.1 问题 根据苹果MVC设计模式的思想原则实现一个简单的翻牌游戏,功能如下: 1)界面上随机摆放12张背面朝上的纸牌,界面效果如图-1所示: 图- 1 2)点击纸牌可以使纸牌翻页,翻 ...

  4. C#实现union以及lock的使用

    1.什么是Union类型数据 联合(Union)是一种特殊的类,一个联合中的数据成员在内存中的存储是互相重叠的.每个数据成员都在相同的内存地址开始. 分配给联合的存储区数量是“要包含它最大的数据成员” ...

  5. SecureCRT rz和sz命令不可用,安装lrzsz

    1.从网站下载 lrzsz-x.xx.xx.tar.gz2.解压文件[root@localhost src]# tar zxvf lrzsz-0.12.20.tar.gz3.安装[root@local ...

  6. ubuntu13 安装并配置ssh,交换密钥

    1.下载安装sudo apt-get install ssh,若出现问题,可选择安装openssh-server,或ssh-server等 2.启动ssh,命令:sudo /etc/init.d/ss ...

  7. Roman to Integer

    Given a roman numeral, convert it to an integer. Input is guaranteed to be within the range from 1 t ...

  8. 转:Struts标签checkbox使用总结(默认选择设置)

    在使用struts标签html:checkbox 的时候,如何让checkbox框默认是选中的,一般情况 下都是当formbean里面该property的值和标签上value给定的值相等的时候,生成的 ...

  9. tty驱动程序框架

    tty驱动程序框架 一.TTY概念解析 在Linux系统中,终端是一类字符型设备,它包括多种类型,通常使用tty来简称各种类型的终端设备. 1.1串口终端(/dev/ttyS*) 串口终端是使用计算机 ...

  10. 关于kafka连接不上别的机器问题Connection refused

    1.确认config里面的server.properties 正确(包括IP+端口2181记得看清,用hostname的话记得etc/hostnames下面查看全部配置上没,建议用hostname方便 ...