比如有这样一个需求,一张表格(User_Salary)包含每个人(UserName)每个月份(Month)发的薪水(Salary)
求这样一个结果集:每个人每月所发薪水及累计所得薪水和,如下表
UserName | Month | Salary |
AAA | 2010/12 | 1000 |
AAA | 2011/01 | 2000 |
AAA | 2011/02 | 3000 |
BBB | 2010/12 | 2000 |
BBB | 2011/01 | 2500 |
BBB | 2011/02 | 2500 |
结果
UserName | Month | Salary | Cumulation |
AAA | 2010/12 | 1000 | 1000 |
AAA | 2011/01 | 2000 | 3000 |
AAA | 2011/02 | 3000 | 6000 |
BBB | 2010/12 | 2000 | 2000 |
BBB | 2011/01 | 2500 | 4500 |
BBB | 2011/02 | 2500 | 7000 |
当然这个结果在Excel中十分好实现,只需要一个公式就好:
注意G2的公式一定要保持第一个列不动所以就是$F$2:F2,然后向下拖一下就可以,但是,这只适用于数据固定的情况下,试想,如果有100个员工的数据,岂不是要拖100下。当然也可能有其他办法,这个我就不知道了。
下面,如果用sql实现能有什么办法呢?首先想到的是游标。
对游标的确可以实现,写程序也可以实现,因为他们的思想是一样的:判断一下名字是不是已经遍历过了,如果遍历过了,就累加一下,如果没有就从0加起。这样很好理解,但是写的很费时,其实一条sql语句就可以实现的,那就是子查询。
create table User_Salary (UserName nvarchar(200), Month nvarchar(20), Salary int) go insert into User_Salary (UserName,Month,Salary ) values('AAA','2010/12',1000) insert into User_Salary (UserName,Month,Salary ) values('AAA','2011/01',2000) insert into User_Salary (UserName,Month,Salary ) values('AAA','2011/02',3000) insert into User_Salary (UserName,Month,Salary ) values('BBB','2010/12',2000) insert into User_Salary (UserName,Month,Salary ) values('BBB','2011/01',2500) insert into User_Salary (UserName,Month,Salary ) values('BBB','2011/02',2500) go select UserName,Month,Salary, Cummulation=( select SUM(Salary) from User_Salary i where i.UserName=o.UserName and i.Month<=o.Month ) from User_Salary o order by 1,2 go drop table User_Salary
大家知道SQL查询的结果是面向集合,而这种嵌套的子查询恰恰就是在整个结果集返回之前做的对于每一行的运算。也许这样的写法不是很容易理解,那么下面这个写法应该容易理解多了。
create table User_Salary (UserName nvarchar(200), Month nvarchar(20), Salary int) go insert into User_Salary (UserName,Month,Salary ) values('AAA','2010/12',1000) insert into User_Salary (UserName,Month,Salary ) values('AAA','2011/01',2000) insert into User_Salary (UserName,Month,Salary ) values('AAA','2011/02',3000) insert into User_Salary (UserName,Month,Salary ) values('BBB','2010/12',2000) insert into User_Salary (UserName,Month,Salary ) values('BBB','2011/01',2500) insert into User_Salary (UserName,Month,Salary ) values('BBB','2011/02',2500) go select A.UserName,A.Month,MAX(A.Salary) as Salary,SUM (B.Salary) as Accumulation from User_Salary A inner join User_Salary B ON A.UserName = B.UserName where B.Month <= A.Month group by A.UserName,A.Month order by A.UserName,A.Month go drop table User_Salary
这样用联合的方式就好理解一些,其实这样就是把每一行对应比他月份小的值分成一组,然后进行运算,如果这样不明白,那么下面的sql会使这个查询更加好理解:
select A.*,B.* from User_Salary A inner join User_Salary B ON A.UserName = B.UserName where B.Month <= A.Month order by 1,2
这样的结果就显而易见了
这就是子查询相关的递归(可以这么说?)算法。
子查询执行计划
join执行计划
通过上述2个执行计划,虽然执行计划不同,但大体一致,这其中的区别我就不太明白了(不知道是先排序再join好 还是先join在排序好,但是我个人觉得第二种比较容易理解.).
源代码:
create table User_Salary (UserName nvarchar(200), Month nvarchar(20), Salary int) go insert into User_Salary (UserName,Month,Salary ) values('AAA','2010/12',1000) insert into User_Salary (UserName,Month,Salary ) values('AAA','2011/01',2000) insert into User_Salary (UserName,Month,Salary ) values('AAA','2011/02',3000) insert into User_Salary (UserName,Month,Salary ) values('BBB','2010/12',2000) insert into User_Salary (UserName,Month,Salary ) values('BBB','2011/01',2500) insert into User_Salary (UserName,Month,Salary ) values('BBB','2011/02',2500) go select UserName,Month,Salary, Cummulation=( select SUM(Salary) from User_Salary i where i.UserName=o.UserName and i.Month<=o.Month) from User_Salary o order by 1,2 select A.UserName,A.Month,MAX(A.Salary) as Salary,SUM (B.Salary) as Accumulation from User_Salary A inner join User_Salary B ON A.UserName = B.UserName where B.Month <= A.Month group by A.UserName,A.Month order by A.UserName,A.Month go drop table User_Salary