My Life My Dream!

守信 求实 好学 力行
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
/*
功能:按报表格式显示(行转列、定制、批量数据产生)
作者:王召冠
时间:2010-01-25 20:10
说明:此例子也是多种sql技术的综合应用

问题:假设有张考勤表(tb)如下:
姓名     考勤日期      考勤次数
张三    2010-01-01        2
张三    2010-01-02        3
张三    2010-01-03        1
张三    2010-01-01        1
李四    2010-01-02        2
李四    2010-01-03        2

想变成(得到如下结果):按指定时间段显示考勤报表
姓名  2010-01-01 2010-01-02 2010-01-03 2010-01-04  ... 汇总     
---- ---------- ---------- ---------- ----------      ----
李四        0           2          2          0        4
张三        3           3          1          0        7
汇总        3           5          3          0        11
----------------------------------------------------------
*/

CREATE TABLE tb(vName NVARCHAR(10), dtDate DATETIME, iNum INT)
GO

INSERT  INTO tb 
VALUES  ( '张三''2010-01-01'2 )
INSERT  INTO tb
VALUES  ( '张三''2010-01-02'3 )
INSERT  INTO tb
VALUES  ( '张三''2010-01-03'1 )
INSERT  INTO tb
VALUES  ( '张三''2010-01-01'1 )
INSERT  INTO tb
VALUES  ( '李四''2010-01-02'2 )
INSERT  INTO tb
VALUES  ( '李四''2010-01-03'2 )
GO

--SELECT    *
--
FROM    tb
    
--SQL SERVER 动态SQL
DECLARE @dtBegin DATETIME, @dtEnd DATETIME
SELECT    @dtBegin = '2010-01-01', @dtEnd = '2010-01-31'

SET NOCOUNT ON

/*
此处代码功能说明:
    根据指定的时间段,快速生成一个不间断的时间序列表
具体实现特性说明:
    每循环一次生成2的n次方条记录,减少循环次数。
注:
    从实际应用上来说,在此处没有太明显的效果和意义;仅仅体现一种优化算法而已。
*/
DECLARE @iRank INT 
SET @iRank = 1
DECLARE @tmp TABLE(dtDate DATETIME)

INSERT INTO @tmp ( dtDate ) VALUES ( @dtBegin ) 
WHILE DATEADD(DAYPOWER(2, (@iRank-1)), @dtBegin<= @dtEnd
BEGIN
    
INSERT INTO @tmp ( dtDate )
    
SELECT    DATEADD(DAYPOWER(2, (@iRank-1)), dtDate)
    
FROM    @tmp 
    
WHERE    DATEADD(DAYPOWER(2@iRank-1), dtDate) <= @dtEnd
    
    
SET @iRank = @iRank + 1
END

/*
此处代码功能说明:
    根据时间序列表,生成动态SQL
注:
    之所以将动态sql分到两个变量里,主要是尽量避免8000个字符的限制
*/
DECLARE @sql_a VARCHAR(8000)
SET @sql_a = 'select vName'
SELECT  @sql_a = @sql_a 
                
+ ', sum(case dtDate when ''' + CONVERT(NVARCHAR(10), dtDate, 120)
                
+ ''' then iNum else 0 end) [' + CONVERT(NVARCHAR(10), dtDate, 120+ ']'
FROM    @tmp AS a
SET @sql_a = @sql_a + ', sum(iNum) as iTotal from tb group by vName'
--EXEC(@sql_a)

DECLARE @sql_b VARCHAR(8000)
SET @sql_b = 'select ''total'''
SELECT    @sql_b = @sql_b + ', isnull((select sum(iNum) from tb where dtDate=''' + CONVERT(NVARCHAR(10), dtDate, 120+ '''), 0)'
FROM    @tmp
ORDER BY dtDate
SET @sql_b = @sql_b + ', (select sum(iNum) from tb)'
--EXEC(@sql_b)
EXEC('select * from (' + @sql_a + ' union ' + @sql_b + ') as a order by a.iTotal'
GO