SqlServer如何生成动态交叉表查询 VB+MS SqlServer,是我们目前开发数据库应用系统最常用的模式,翻翻以前的老帖子,有一些SqlServer的问题经常被提出来,但正确解答甚少,现把我对这些问题的见解贴出来,这次先讲讲动态交叉表的问题 为了说明问题,我们用SqlServer自带的事例数据库(Northwind)来进行验证,所有的例子请放到Northwind中运行,我可能会省略Use语句,所引用的表,都是Northwind中的,下面我就不再说明了 我这里指的交叉表,就是象Access的交叉表查询一样的效果,比如Employees表中City字段代表了城市的名称,TitleOfCourtesy代表称呼,我们希望按照City和TitleOfCourtesy的情况来统计ReportsTo字段的合计数(本统计没有任何实际意义,只是挑选一些记录包含重复内容的字段来说明情况),并显示成以下格式:(TitleOfCourtesy作为行,City作为列)
探讨这个问题之前,我们首先来看一下如何建立静态的交叉表,也就是说列数固定的交叉表,这种情况其实只要一句简单的Select查询就可以搞定: SELECT TitleOfCou rtesy, 其中利用了CASE语句判断,如果是相应的列,则取需要统计的ReportsTo数值,否则取NULL,然后再合计 b、假如在视图的设计界面保存以上的查询,则会报错“没有输出列”,从而无法保存,其实只要在查询前面加上一段:Create View ViewName AS ...,ViewName是你准备给查询起的名称,...就是我们的查询,然后运行一下,就可以生成视图了,对于其他一些设计器不支持的语法,也可以这样保存。 以上查询作用也很大,对于很多情况,比如按照季度统计、按照月份统计等列头内容固定的情况,这样就行了,但往往大多数情况下列头内容是不固定的,象City,用户随时可能删除、添加一些城市,这种情况,我们就需要用存储过程来解决: 总体思路其实很简单,首先检索列头信息,形成一个游标,然后遍历游标,将上面查询语句里Case判断的内容用游标里的值替代,形成一条新的Sql查询,然后执行,返回结果,就可以了,以下是我写的一个存储过程,供大家参考: CREATE procedure CorssTab DECLARE @strSql as varchar(1000), @strTmpCol as varchar(100) OPEN corss_cursor EXECUTE(@strsql) --执行 IF @@error <>0 RETURN @@error --如果出错,返回错误代码 end 几点说明: CorssTab @strTabName = 'Orders', @strCol = 'DATEPART(yy, OrderDate)',@strGroup = 'CustomerID', @strNumber = 'OrderID', @strSum = 'Count' 上面这条语句统计了NorthWind中Orders表里每个客户年度订单数量,大家可以运行试一下效果,虽然列头显示的名称不恰当,但基本效果出来了,相信大家通过对我的代码再作简单修改,可以达到满意的交叉表效果。 下次我再讲讲,如何给查询的记录集自动加行号
动态SQL的使用例子, 行列转换 drop table #test create table #test(name nvarchar(20),type nvarchar(20),category nvarchar(20)) select * from #test insert into #test(name,type,category) values ('n1','t1','c1'); insert into #test(name,type,category) values ('n2','t1','c2'); insert into #test(name,type,category) values ('n3','t2','c1'); insert into #test(name,type,category) values ('n4','t3','c3'); insert into #test(name,type,category) values ('n5','t2','c4'); insert into #test(name,type,category) values ('n6','t3','c5'); insert into #test(name,type,category) values ('n1','t1','c1'); --select category,name,sum(case when type='t1' then 1 else 0 end),sum(case when type='t2' then 1 else 0 end),sum(case when type='t3' then 1 else 0 end) from #test group by name,category --如果type不固定 --使用動態SQL語句 Declare @S Varchar(8000) Select @S = 'Select category, name' Select @S = @S + ', SUM(Case type When ''' + type + ''' Then 1 Else 0 End) As ' + type From #TEST Group By type Select @S = @S + ' From #TEST Group By category, name Order By category, name' print @S EXEC(@S) GO
--测试数据 行转列 Create table test (name char(10),km char(10),cj int) insert test values('张三','语文',80) insert test values('张三','数学',86) insert test values('张三','英语',75) insert test values('李四','语文',78) insert test values('李四','数学',85) insert test values('李四','英语',77) --查询 declare @sql varchar(8000),@s1 varchar(8000) select @sql = '',@s1='' select @sql = @sql+ ',['+km+']=sum(case km when '''+km+''' then cj else 0 end)' ,@s1=@s1+',sum(case km when '''+km+''' then cj else 0 end)/sum(case km when '''+km+''' then 1 else 0 end)' from test group by km exec('select name=case grouping(name) when 1 then ''全班总分'' else name end'+@sql+',小计=sum(cj) from test group by name with rollup union all select ''全班平均分'''+@s1+',sum(cj)/count(distinct name) from test') go --删除测试 drop table test
--MS SQL2000下月份不固定的動態寫法 Create Table TEST (class Nvarchar(10), name Nvarchar(10), 年份 Int, [1月] Varchar(10), [2月] Varchar(10), [3月] Varchar(10)) Insert TEST Select N'一班',N'张三',2007,'5元','2元','5元' Union All Select N'一班',N'李四',2006,'3元','0元','1元' Union All Select N'二班',N'王五',2007,'0元','0元','1元' GO Declare @S Nvarchar(4000) Select @S = '' Select @S = @S + ' Union Select class, name, 年份, ''' + Name + ''' As 月份, [' + Name + '] As 元 From TEST ' From SysColumns Where ID = OBJECT_ID('TEST') And Name Like '%月' Order By Name Select @S = Stuff(@S, 1, 7, '') Print @S EXEC(@S) GO Drop Table TEST
--动态月份2005 处理如下: --测试环境 create table tb_tb(class varchar(10),name varchar(10),年份 varchar(10),[1月] varchar(10),[2月] varchar(10),[3月] varchar(10)) insert into tb_tb select '一班','张三','2007','5元','2元','5元' union all select '一班','李四','2006','3元','0元','1元' union all select '二班','王五','2007','0元','0元','1元' --计算月份: declare @月份 varchar(100) set @月份=''; select @月份=@月份+',['+name+']' from sys.columns where object_id=object_id('tb_tb') and name like '%月' set @月份=stuff(@月份,1,1,'') --交叉表处理 exec(' select * from tb_tb unpivot ( 金额 for 月份 in ('+@月份+') ) unpt where 金额<>''0元'' ') --删除测试环境 drop table tb_tb
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]()
把列变成行的sql语句 线有如下表: 科目 分数 姓名 语文 69 婵娟 语文 69 李慧 一条sql语句,查询结果是 李慧 婵娟 董兆 create table k(科目 varchar(50),分数 int,姓名 varchar(50)) declare @s varchar(8000)
应用SQL交叉表实现行列转换 --数据结构
--数据体
--SQL语句 Declare@SqlStrnvarchar(2000)
--构建Sql语句,生成2004级2班 2004年的考虑成绩表 Select@SqlStr='selectGrade,ExamYear,Name,'
--生成条件选择语句,使用Distinct把所有科目全部列出,当然,可以加上条件,如:仅某一年度的考试科目 Select@SqlStr=@SqlStr+'SUM(CASESubjectWHEN'''+Subject+'''THENScoreELSE0END)AS'''+Subject+''','from(SelectDistinctSubjectfromExamResultwhereExamYear=2004)AsTemTable
--补全Sql语句,并使用GroupBy对重复的记录进行汇总 Select@SqlStr=left(@SqlStr,Len(@SqlStr)-1)+'fromExamResultwhereExamYear=2004GroupByName,Grade,ExamYear'
--执行Sql Exec(@SqlStr) Go
--执行结果
1 2004级2班 2004 付超 89 85 56 2 2004级2班 2004 李明 45 100 75 3 2004级2班 2004 王强 78 90 89 行列转换例子 一、現有一個表TB1,其字段如下:(Name:代表姓名,Province:代表省份,Score:代表業務顧客數量) 得到下面的結果: 注意:省份必須根據TB1表中所有出現的省份進行統計 create table A --测试 select @s = @s +','+Province+'= sum(case province when'''+province+'''then score else 0 end)' exec ('select name '+@s+',sum(Score) As Total from A group by Name order by Name') --测试结果 --测试结束
insert #XS_REP_CLIENTPROD_CHX --查询 --删除测试 /*--测试结果 c_PCID C_CUSTID c_CustName 苹果 葡萄 --*/ 三 行列数 create table fbill(fauxqty int,Fdeptidname varchar(20),fsupplyidname varchar(20)) declare @sql varchar(8000) select @sql=@sql+','+Fdeptidname+'=sum(case Fdeptidname when '''+Fdeptidname+''' then fauxqty else 0 end)' set @sql='select fsupplyidname as 客户'+@sql+' from fbill group by fsupplyidname' /* drop table fbill 经常用到的交叉表问题,一般用动态SQL能生成动态列!
--原贴 http://community.csdn.net/Expert/topic/4200/4200386.xml?temp=.4856989 原始表如下格式: 根据Class的值,按日期分别统计出CallCount1,CallCount2,CallCount3。 --创建测试环境 --结果 CallDate CallCount1 CallCount2 CallCount3 --删除测试环境 drop table T
详细介绍SQL交叉表的实例 很简单的一个东西,见网上好多朋友问“怎么实现交叉表?”,以下是我写的一个例子,数据库基于SQL SERVER 2000。 交叉表实例 建表: 在查询分析器里运行: CREATE TABLE [Test] ( [id] [int] IDENTITY (1, 1) NOT NULL , [name] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL , [subject] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL , [Source] [numeric](18, 0) NULL ) ON [PRIMARY] GO INSERT INTO [test] ([name],[subject],[Source]) values (N'张三',N'语文',60) INSERT INTO [test] ([name],[subject],[Source]) values (N'李四',N'数学',70) INSERT INTO [test] ([name],[subject],[Source]) values (N'王五',N'英语',80) INSERT INTO [test] ([name],[subject],[Source]) values (N'王五',N'数学',75) INSERT INTO [test] ([name],[subject],[Source]) values (N'王五',N'语文',57) INSERT INTO [test] ([name],[subject],[Source]) values (N'李四',N'语文',80) INSERT INTO [test] ([name],[subject],[Source]) values (N'张三',N'英语',100) Go 交叉表语句的实现: 用于:交叉表的列数是确定的 select name,sum(case subject when '数学' then source else 0 end) as '数学', sum(case subject when '英语' then source else 0 end) as '英语', sum(case subject when '语文' then source else 0 end) as '语文' from test group by name --用于:交叉表的列数是不确定的 declare @sql varchar(8000) set @sql = 'select name,' select @sql = @sql + 'sum(case subject when '''+subject+''' then source else 0 end) as '''+subject+''',' from (select distinct subject from test) as a select @sql = left(@sql,len(@sql)-1) + ' from test group by name' exec(@sql) go 运行结果:
将日期转换为字符串作为列以及含有行列合计的交叉表: 演示代码如下: if exists (select 1 from sysobjects where id = object_id('test') and type ='U') drop table test go --建立测试环境 set nocount on --drop table test create table test(model varchar(20),date datetime ,qty int) insert into test select '8A09208G','2009-5-8 12:23:24.550','100' insert into test select '8A09208G','2009/5/10 10:18:46.480','500' insert into test select '8B02806G','2009/5/8 13:24:16.123','1000' insert into test select '8B02806G','2009/5/9 15:26:37.123','2000' insert into test select '8B02806G','2009/5/10 03:08:56.123','1000' insert into test select '8C00184G','2009/5/10 08:09:43.123','2000' insert into test select '8D18302G','2009/5/10 18:26:37.123','3000' insert into test select '8E04305G','2009/5/11 20:19:21.123','2500' insert into test select '8E04305G','2009/5/12 16:24:54.123','1000' insert into test select '8F00203G','2009/5/12 21:53:45.123','1500' go --测试 --日期范围选择 declare @startdate datetime --查询的起始日期 declare @enddate datetime --查询的最后日期 set @startdate =cast('2009/5/8 15:13:12' as datetime) set @enddate = cast('2009/5/10' as datetime) --去掉时间部分的影响,保留日期部分,时间变为00:00:00.000, --这样同一天不同时间的值变成相同的了 set @startdate=cast(convert(char(10),@startdate,120) as datetime) set @enddate=cast(convert(char(10),@enddate,120) as datetime) --select @startdate --生成动态列(日期数据转换为要求的字符格式) declare @sql varchar(8000) set @sql= 'select model =CASE WHEN GROUPING(model)=0 THEN model ELSE''总计'' END' --注意字符值不能用双引号,此处用了两个单引号''总计'' --产生动态日期列 declare @sql01 varchar(8000) set @sql01='' select @sql01=@sql01+', ['+strdate+']= sum(case when convert(char(6),date,12)='+strdate+ ' then qty else 0 end)' from (select distinct top 100 percent convert(char(6),date,12) as strdate from test where (cast(convert(char(10),date,120) as datetime) >= @startdate and cast(convert(char(10),date,120) as datetime) <= @enddate) order by convert(char(6),date,12)) as selecteddate --产生最终的查询语句 select @sql=@sql+@sql01 --加上model的合计数及查询有关的表的相关部分 set @sql=@sql +', 合计=sum(qty) from test where ((convert(char(8),date,112)>=' set @sql=@sql+convert(char(8),@startdate, 112)+') and (convert(char(8),date,112)<= ' set @sql=@sql+convert(char(8),@enddate, 112)+')) group by model with rollup' --select @sql ---输出查询语句 --执行查询 exec( @sql) /*输出结果 model 090508 090509 090510 合计 -------------------- ----------- ----------- ----------- ----------- 8A09208G 100 0 500 600 8B02806G 1000 2000 1000 4000 8C00184G 0 0 2000 2000 8D18302G 0 0 3000 3000 总计 1100 2000 6500 9600 */ --删除测试环境 drop table test set nocount off
-------------------------------全文结束------------------------------------------
|
|
来自: ThinkTank_引擎 > 《交叉表》