本文原本是为了说明游标在某些特定环境下的用途,突然发现用来说明临时表、表变量和 CTE 的关系更为合理一些。
本文的例子给了一串数字,是为了求得同组中所有数字的乘积,包括累计叠加等等,这样的案例通过一般的分组方式很难办到,而通过游标更容易实现。
创建一个测试 表
CREATE TABLE groups
(groupid VARCHAR(10), val INT NULL )
INSERT INTO groups VALUES ('a',2);
INSERT INTO groups VALUES ('a',3);
INSERT INTO groups VALUES ('a',1);
INSERT INTO groups VALUES ('b',4);
INSERT INTO groups VALUES ('b',6);
INSERT INTO groups VALUES ('b',1);
INSERT INTO groups VALUES ('c',10);
INSERT INTO groups VALUES ('c',3);
INSERT INTO groups VALUES ('d',7);
-- 通过临时表和游标的方法加以实现----------------------------------------------------------------------
create table #result (groupid varchar(10),product bigint);
declare @groupid varchar(10),
@prvgroupid varchar(10),
@val int,
@product bigint;
declare c cursor fast_forward for select groupid,val from groups order by groupid;
open c;
fetch next from c into @groupid,@val;
select @prvgroupid=@groupid,@product=1;
while @@fetch_status=0
begin
if @groupid<>@prvgroupid
begin
insert into #result values(@prvgroupid,@product);
select @prvgroupid=@groupid,@product=1;
end
set @product=@product*@val;
fetch next from c into @groupid,@val;
end
if @prvgroupid is not null
insert into #result values(@prvgroupid,@product);
close c;
deallocate c;
select groupid,product from #result
-- 通过表变量和游标的方式加以实现,从这里看的话表变量和临时表基本雷同-----------------------------------------
declare @groupid varchar(10),@prvgroupid varchar(10),@val int,@product bigint;
declare c cursor fast_forward for select groupid,val from groups order by groupid;
open c;
fetch next from c into @groupid,@val;
select @prvgroupid=@groupid,@product=1;
while @@fetch_status=0
begin
if @groupid<>@prvgroupid
begin
insert into #result values(@prvgroupid,@product);
select @prvgroupid=@groupid,@product=1;
end
set @product=@product*@val;
fetch next from c into @groupid,@val;
end
if @prvgroupid is not null
insert into #result values(@prvgroupid,@product);
close c;
deallocate c;
select groupid,product from #result
-- 通过CTE 的方式加以实现-----------------------------------------------------------------------
with all_but_val as
( select a.groupid,a.val*b.val accum_val
from groups a,groups b
where a.groupid=b.groupid and a.val>b.val
),
but_val as
(select * from groups
where groupid in (select groupid
from groups
group by groupid
having count(*)=1
)
),
all_val as
( select * from all_but_val
union
select * from but_val
)
select groupid,max(accum_val) from all_val
group by groupid