您的位置:首页 > 其它

一种比较复杂的交叉数据报表的作法及其复杂度的简析

2007-10-10 12:00 190 查看
    有时候需要旋转查询结果以便在水平方向显示列,而在垂直方向显示行。就是所谓的创建 PivotTable、创建交叉数据报表或旋转数据。交叉数据报表在做具体应用的查询分析的时候是非常有用的。有些时候交叉数据报表又叫透视表。
本文向大家介绍一种比较复杂的透视表的作法,并对其作时间复杂度分析。 为演示方便,在本文中用到的表全部是临时表,使用的是SQL Server 2000。临时表以“#”开头,用户产生的临时表并不放在本用户创建的数据库实例当中,而是放在系统自己的tempdb数据库中,在用户与数据库断开连接后,数据库服务器会自动删除该临时表。
一、 题目来源如下:
对某生产部门的生产情况进行查询,此查询按用户、年份、季度进行。查询某用户、在某年每一季度的平均生产量和该季度的累计生产量。为了简明起见,在表中只有A用户,查询A用户从2002年到2003年的四个季度的“平均生产量”和“累计生产量”。其数据表如下(#temp1):
NULL 年份 季度 平均生产量 累计生产量
A  2002  1   200        600       
A  2002  2     140        420       
A  2002  3   165        495       
A  2002  4   190        570       
A  2003  1   100        300       
A  2003  2    130        390       
A  2003  3   170        510       
A  2003  4   180        540       (#temp1)
按照交叉报表的要求季度列应当按行显示,对每一季度显示其平均生产量和累计生产量。
二、 生成交叉报表过程:

2.1、 均生产量和累计生产量分开表示:

“select 用户,年份,季度,平均生产量,'0' as 累计生产量
into #temp2
from #temp1
union
select 用户,年份,季度,累计生产量,'1' 
from #temp1“

以上语句的作用是设置标记来区分“平均生产量”和“累计生产量”。其中“0”表平均生产量,“1”表示累计生产量。得到数据表如下(#temp2):

用户 年份  季度 平均生产量 累计生产量
A  2002    1 200        0
A  2002    1 600        1
A  2002 2 140        0
A  2002 2 420        1
A  2002 3 165        0
A  2002 3 495        1
A  2002 4 190        0
A  2002 4 570        1
A  2003 1 100        0
A  2003 1 300        1
A  2003 2 130        0
A  2003 2 390        1
A  2003 3 170        0
A  2003 3 510        1
A  2003 4 180        0
A  2003 4 540        1    (#temp2)

“select 用户,年份,季度+累计生产量 as 季度flag,平均生产量=case 累计生产量 when 0 then 平均生产量 else 0 end,
累计生产量=case 累计生产量 when 1 then 平均生产量 else 0 end 
into #temp3
from #temp2“

以上语句的作用是设置标记来区分某季度的“平均生产量”和“累计生产量”。其中“10”表示 “季度1” 的平均生产量,“11”表示 “季度1”的累计生产量。得到数据表如下(#temp3):

用户 年份 季度flag 平均生产量 累计生产量
A  2002 10    200 0
A  2002 11 0 600
A  2002 20 140 0
A  2002 21 0 420
A  2002 30 165 0
A  2002 31 0 495
A  2002 40 190 0
A  2002 41 0 570
A  2003 10 100 0
A  2003 11 0 300
A  2003 20 130 0
A  2003 21 0 390
A  2003 30 170 0
A  2003 31 0 510
A  2003 40 180 0
A  2003 41 0 540    (#temp3)


2.2、生成主SQL语句,执行主SQL语句形成最后的交叉报表,

declare @sql varchar(8000)
select @sql='select NULL as 用户, NULL as 年份'
select @sql=@sql+','+'''第'+jd+'季度'''+','+'''第'+jd+'季度'''
from(select distinct jd from #temp_jd)a
select @sql=@sql+' union '
select @sql=@sql+'select NULL ,NULL '
select @sql=@sql+',''平均生产量'''+',''累计生产量'''
from(select distinct jd from #temp_jd)a
select @sql=@sql+' union '
select @sql=@sql+'select 用户,年份'
select @sql=@sql+', ['+jd+'0]'+'=convert(varchar,sum(case 季度flag when '+jd+'0
then 平均生产量 else 0 end))'+', ['+jd+'1]'+'=convert(varchar,sum(case 季flag
when '+jd+'1 then 累计生产量 else 0 end))'
    from(select jd from #temp_jd)a
select @sql=@sql+ ' from #temp3 group by 用户,年份'
exec(@sql)

生成交叉报表如下:

NULL  NULL  第1季度   第1季度     第2季度  第2季度    第3季度   第3季度     第4季度  第4季度
NULL  NULL 平均生产量 累计生产量   平均生产量 累计生产量  平均生产量 累计生产量   平均生产量 累计生产量
A   2002 200  600  140   420 165   495   190   570
A   2003 100  300  130   390 170   510   180   540

三、说明:
1、在组合主SQL语句的过程中用到了临时表#temp_jd,此表的作用是,辅助生成主SQL语句,并动态向主SQL语句提供季度分量。
2、在给主SQL语句赋值的过程中使用select而非set。这是因为主SQL语句是动态生成的,因为在将季度列显示为行的过程中使用的如下语句:
select @sql=@sql+',['+jd+'0]'+'=convert(varchar,sum(case 季度flag when '+jd+'0
then 平均生产量 else 0 end))'+', ['+jd+'1]'+'=convert(varchar,sum(case 季flag
when '+jd+'1 then 累计生产量 else 0 end))'
    from(select jd from #temp_jd)a

该语句可理解为 select @sql=@sql+"***" from (select jd from #temp_jd)a
其中(select jd from #temp_jd)a可理解为将select jd from #temp_jd 返回的记录集起一个别名a 
由此我们不难理解,此语句起的是循环的作用。可以按照#temp_jd的季度分量动态生成交叉报表的列。假如#temp_jd中有1、2季度则只生成1、2季度的“平均生产量”和“累计生产量”,以此类推。而#temp_jd的形成是与我们此文中的主数据表#temp1有关的,即#temp_jd中的季度分量的个数是对#temp1中季度分量集合的计数。


四、其时间复杂度分析:
    SQL语句中的查询语句是:
select [ALL|DISTINCT]<目标列表达式>[,<目标列表达式>,...] 
from <表名|视图名>[,<表名|视图名>,...]
[where <条件表达式1>[,<条件表达式2>,...]]
[group by <列名> [having <条件表达式>]
[order by <列名>]。 其执行原理如下:
按照Where 语句中的 条件表达式 从数据源中找到符合条件的元组(记录),返回的是记录的集合,在返回记录的同时对select 语句进行操作,如果有group by 语句,则按group by 语句中的 条件表达式对聚合函数进行分组操作。 所以其执行过程可理解如下:

While( where条件表达式满足)
       {
语句1:从 from 指定的数据源中返回记录;
语句2:对 语句1中返回的记录进行select 操作;

语句3:if(存在 group by 条件表达式 )
{
  语句1:按group by 条件表达式执行聚合函数;
}
}

1、对上文中生成#temp2,#temp3 的过程其时间复杂度为0(n)。
2、形成主SQL语句的过程,在动态生成季度列的过程中,其时间复杂度为0(n)。
3、执行主SQL语句,其时间复杂度为0(n)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: