最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501
当前位置: 首页 - 科技 - 知识百科 - 正文

数据库表的转置

来源:懂视网 责编:小采 时间:2020-11-09 16:04:53
文档

数据库表的转置

数据库表的转置:问题 在工作中会遇到这样的问题。 一个学生有多科成绩,每科成绩记录在表中为一条记录,那么查询出来之后,要把一个学生所有科目的成绩打印成一行,即把表中的多条记录合成一条记录。 在销售管理中,每个月的销售数字在表中表示为一条记录,在统计的时候,需
推荐度:
导读数据库表的转置:问题 在工作中会遇到这样的问题。 一个学生有多科成绩,每科成绩记录在表中为一条记录,那么查询出来之后,要把一个学生所有科目的成绩打印成一行,即把表中的多条记录合成一条记录。 在销售管理中,每个月的销售数字在表中表示为一条记录,在统计的时候,需

问题 在工作中会遇到这样的问题。 一个学生有多科成绩,每科成绩记录在表中为一条记录,那么查询出来之后,要把一个学生所有科目的成绩打印成一行,即把表中的多条记录合成一条记录。 在销售管理中,每个月的销售数字在表中表示为一条记录,在统计的时候,需

问题

在工作中会遇到这样的问题。

一个学生有多科成绩,每科成绩记录在表中为一条记录,那么查询出来之后,要把一个学生所有科目的成绩打印成一行,即把表中的多条记录合成一条记录。

在销售管理中,每个月的销售数字在表中表示为一条记录,在统计的时候,需要按照产品,在一行中输出所有月份的销售统计。

当然对于一些比较死板的人来说可以说那我就做多个字段来记录就是了,但是这样的扩展性就很差了。如果科目变了呢,你不得改表结构吗?

准备数据

下面我们以销售为例,首先创建如下的表。

create table Orders
(
 ProductID int,
 OrderMonth int,
 SubTotal money
)

表中的每一行表示一个产品每月的销售情况。

然后,插入若干数据。

insert into Orders ( ProductID, OrderMonth, SubTotal )
select 1, 5, 100.00 union all
select 1, 6, 100.00 union all
select 2, 5, 200.00 union all
select 2, 6, 200.00 union all
select 2, 7, 300.00 union all
select 3, 5, 400.00 union all
select 3, 5, 400.00 

此时,表中的数据如下所示。

现在,我们需要统计每种产品在每个月的销售情况。

SQL Server2005 中的方式

使用在 SQL Server2005 中提供的 Pivot 进行转置。

SELECT ProductID, [5] AS 五月, [6] AS 六月, [7] AS 七月
FROM
Orders PIVOT
(
SUM (Orders.SubTotal)
FOR Orders.OrderMonth IN
( [5], [6], [7] )
) AS pvt
 
ORDER BY ProductID;

Orders PIVOT 表示对表 Orders 进行转置操作,这个表称为输入表。

Orders.OrderMonth 称为透视列(pivot_column),FOR Orders.OrderMonth IN ( [5], [6], [7] ) 表示针对表中 OrderMonth 为 5,6,7 的月进行分组之后转置。

SUM (Orders.SubTotal) 表示针对每组的 SubTotal 进行分组求和。SubTotal 列称为值列。

SELECT ProductID, [5] AS 五月, [6] AS 六月, [7] AS 七月 语句中的 ProductId 与 OrderMonth 组合在一起完成分组,后面的 [5] AS 五月, [6] AS 六月, [7] AS 七月 分别对应 FOR Orders.OrderMonth IN ( [5], [6], [7] ) 中的 [5], [6], [7] 。

Pivot 实际上按照以下的步骤完成操作:

1. 分组求和,先按照 ProductID,OrderMonth 进行分组求和

SELECT ProductID, OrderMonth, SUM (Orders.SubTotal) AS SumSubTotal
FROM Orders
GROUP BY ProductID,OrderMonth;

得到的结果如下:

2. PIVOT根据FOR OrderMonth IN指定的值5、6、7,首先在结果集中建立名为5、6、7的列,然后从上面的结果中取出 OrderMonth 列中取出相符合的值,分别放置到5、6、7的列中。此时得到的结果集的别名为pvt(见语句中AS pvt的指定)。结果集的内容如下所示。

3. 最后根据SELECT ProductID, [5] AS 五月, [6] AS 六月, [7] AS 七月 FROM的指定,从别名pvt结果集中检索数据,并分别将名为5、6、7的列在最终结果集中重新命名为五月、六月、七月。这里需要注意的是FROM的含 义,其表示从经PIVOT关系运算符得到的pvt结果集中检索数据,而不是从Sales.Orders中检索数据。

Pivot 的语法如下:

SELECT <非透视的列>,
 [第一个透视的列] AS <列名称>,
 [第二个透视的列] AS <列名称>,
 ...
 [最后一个透视的列] AS <列名称>,
FROM
 (<生成数据的 SELECT 查询>)
 AS <源查询的别名>
PIVOT
 ( <聚合函数>(<要聚合的列>)
FOR
 
[<包含要成为列标题的值的列>]
 IN ( [第一个透视的列], [第二个透视的列], ... [最后一个透视的列])
)
 AS <透视表的别名>
<可选的 ORDER BY 子句>;

需要注意的是:如果聚合函数与 PIVOT 一起使用,则计算聚合时将不考虑出现在值列中的任何空值。

SQL Server 2000 中的方式

在 SQL Server 2005 之前,通常需要通过 case 子句来处理。

select ProductID,
 sum( case when OrderMonth = 5 then SubTotal end ) as 五月,
 sum( case when OrderMonth = 6 then SubTotal end ) as 六月,
 sum( case when OrderMonth = 7 then SubTotal end ) as 七月
from Orders
group by ProductID

补充说明:

今天在使用 pivot 的时候,出现一个奇怪的事情,转置居然失败了!

表的结构如下:

create table tbl_marks
(
 markId int identity(1,1) , -- 成绩的关键字
 sid int , -- 学生的标识
 cid int , -- 课程的标识
 
 mark int, -- 成绩
)

表中的数据为:

insert into tbl_marks ( sid, cid, mark ) values ( 1, 1, 100 );
insert into tbl_marks ( sid, cid, mark ) values ( 1, 2, 90 );
insert into tbl_marks ( sid, cid, mark ) values ( 1, 3, 95 );
insert into tbl_marks ( sid, cid, mark ) values ( 2, 1, 60 );
insert into tbl_marks ( sid, cid, mark ) values ( 2, 2, 61 );
insert into tbl_marks ( sid, cid, mark ) values ( 2, 3, 99 );

执行的转置语句如下:

select sid, [1] as [database], [2] as [CSharp], [3] as [Xml]
from tbl_marks pivot
(
 sum( mark )
 for cid in ( [1], [2], [3] )
) as pvt

注意,最后的 as pvt 提供的别名必须要有,虽然没有实际的用途,但是 SQLServer 2005 要求必须提供。

结果如下:

1 100 NULL NULL
1 NULL 90 NULL
1 NULL NULL 95
2 60 NULL NULL
2 NULL 61 NULL
2 NULL NULL 99

应该只有两行的结果,居然是六行,显然没有进行分组!

经过分析,发现在 pivot 中,分组的依据是隐含的,pivot 将对表中除了组函数计算列和转置的列之外所有的字段进行分组,在上边的情况下,组函数对 mark 进行求值,对课程列 cid 进行转置,而表中实际有 4 列,剩下了 markit 和 sid 两列,而 markit 是一个自增长的标识列,所有的行都不相同,这样,实际上的分组并不是在 sid 一个字段上进行的,导致了错误的转置结果。

处理的方法是将表中的列限制在 3 列,排除掉 markit 这个标识列,通过子查询可以轻松处理这个问题。

select sid, [1] as [database], [2] as [CSharp], [3] as [Xml]
from ( select sid, cid, mark from tbl_marks) t pivot
(
 sum( mark )
 for cid in ( [1], [2], [3] )
) as pvt

注意,在 SQLServer 中子查询需要提供一个别名,虽然还是没有什么用途。  

如果使用 case 的话,上边的转置还可以这样写。

select [sid], 
 max( case when cid = 1 then mark end ) as [database],
 min( case when cid = 2 then mark end ) as cSharp , -- 仅仅对课程编号是 2 的课程成绩进行求和,其实课程编号为 2 的成绩仅仅出现了一次
 -- 所以,实际上返回的就是课程 2 的成绩
 avg( case when cid = 3 then mark end ) as xml,
 sum( mark ) as [总分]
from tbl_marks
 
group by [sid] 

在这个例子中,组函数不仅可以使用 sum, 其实使用 max, min, avg 都可以,你知道为什么吗?  

  

 

参考文献

详细的 case 使用说明可以参考 钱途无梁 的 sql 中 case when 语法

http://www.cnblogs.com/qiantuwuliang/archive/2009/06/03/1495770.html

本文主要参考一下文章:

张洪举的文章:在SQL Server 2005中实现表的行列转换

http://blog.csdn.net/zhanghongju/archive/2006/06/02/769445.aspx

MSDN: 使用 PIVOT 和 UNPIVOT

http://technet.microsoft.com/zh-cn/library/ms177410.aspx

Sman Sky :表中数据转置(Pivot)在Sql Server 2000 和Sql Server 2005 的实现

http://www.cnblogs.com/huangbaixun/archive/2008/07/26/1252002.html

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

文档

数据库表的转置

数据库表的转置:问题 在工作中会遇到这样的问题。 一个学生有多科成绩,每科成绩记录在表中为一条记录,那么查询出来之后,要把一个学生所有科目的成绩打印成一行,即把表中的多条记录合成一条记录。 在销售管理中,每个月的销售数字在表中表示为一条记录,在统计的时候,需
推荐度:
标签: 一个 工作 数据
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top