SQLServer透视和逆透视(行转列)

本文介绍SQL中透视和逆透视的基本操作方法,包括如何使用CASE语句、PIVOT函数进行数据列转换,以及UNPIVOT函数实现行转列。通过具体实例展示不同场景下的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

--透视
--======================================================--
use tempdb
--判断是否存在OBJECT_ID('dbo.orders','U')对象,第二个参数U代表是用户创建的
if OBJECT_ID('dbo.orders','U') is not null drop table dbo.orders
create table dbo.orders
(
    orderid int not null primary key,
    empid int not null,
    custid int not null,
    orderdate datetime,
    qty int
)

insert into dbo.orders (orderid,empid,custid,orderdate,qty) values 
    (30001,3,1,'20070802',10),
    (10001,2,1,'20071224',12),
    (10005,1,2,'20071224',20),
    (40001,1,3,'20080109',40),
    (10006,1,3,'20080118',14),
    (20001,2,2,'20080212',12),
    (40005,3,1,'20090212',10),
    (20002,1,3,'20090216',20),
    (30003,2,2,'20090418',15),
    (30004,3,3,'20070418',22),
    (30007,3,4,'20090907',30)

--分组查询
select empid,custid,sum(qty) sumQty from orders group by empid,custid

--1.对雇员进行分组
select empid,sum(qty) from orders group by empid

--可以计算某个顾客被某个雇员销售的数量
select sum(qty) from orders where empid=1 and custid=2

--2.增加顾客的列, 1号顾客
select empid,
(select sum(qty) from orders where empid=o.empid and custid=1) as 一号顾客,
(select sum(qty) from orders where empid=o.empid and custid=2) as 二号顾客,
(select sum(qty) from orders where empid=o.empid and custid=3) as 三号顾客,
(select sum(qty) from orders where empid=o.empid and custid=4) as 四号顾客
from orders o group by empid

--使用case...end,这里的sum(case when custid=1 then qty end)代表只有custid=1的qty做合计
select empid,sum(case when custid=1 then qty end) as 一号顾客,
sum(case when custid=2 then qty end) as 二号顾客,
sum(case when custid=3 then qty end) as 三号顾客,
sum(case when custid=4 then qty end) as 四号顾客
 from orders group by empid

--使用pivot
 use tempdb

select empid,[1] as 顾客1,[2] as 顾客2,[3] as 顾客3,[4] as 顾客4 
from ( --返回透视中用到的列
select empid,qty,custid from orders) t --分组是隐含的, 对表中除了聚合sum(qty)和条件for custid in ([1],[2],[3],[4])的列进行分组
pivot (
    sum(qty) --聚合函数
    for custid in ([1],[2],[3],[4]) --准备做列名(也就是要对custid列的哪些值作为列名)
    ) as p;

select * from orders
--错误的写法(原因没有对要用到透视中的列进行处理).因为orders里面有很多列,除了聚合和条件以下语句还会对empid,orderid,orderdate一起分组,所以结果会得到很多数据
select empid,[1],[2],[3],[4] 
from orders
pivot (
    sum(qty)
    for custid in ([1],[2],[3],[4]) 
    ) as p;

--练习
use TSQLFundamentals2008

select empid,count(1) as 总计,count(case when year(orderdate)='2006' then orderdate end) as '2006年',
 count(case when year(orderdate)='2007' then orderdate end) as '2007年',
  (select count(1) from Sales.Orders where year(orderdate)='2008' and empid=o.empid)as '2008年'
  from Sales.Orders o group by empid

select * from Sales.Orders

--使用pivot
select empid,[2006],[2007],[2008] from (select empid,year(orderdate) as orderyear,orderid from Sales.Orders) t pivot (
    count(orderid) for orderyear in ([2006],[2007],[2008])
) as p
order by empid
--======================================================--

 逆透视:

--逆透视
use tempdb

if object_id('dbo.EmpCustOrders','U') is not null
drop table dbo.EmpCustOrders

select empid,[1],[2],[3],[4] into dbo.EmpCustOrders
from ( 
select empid,qty,custid from orders) t 
pivot (
    sum(qty) --聚合函数
    for custid in ([1],[2],[3],[4]) 
    ) as p;

select * from dbo.EmpCustOrders

--逆透视,列转行
--交叉连接
select * from (
select empid,custid,(case 
        when custid=1 then [1]
        when custid=2 then [2]
        when custid=3 then [3]
        else [4]
        end) as qty from 
dbo.EmpCustOrders e cross join (
    select 1 as custid
    union all
    select 2
    union all
    select 3
    union all 
    select 4
) as c
) as t where qty is not null

--使用unpivot
select empid,custid,qty from dbo.EmpCustOrders unpivot(qty for custid in ([1],[2],[3],[4])) as u

 

<think>好的,我现在需要回答用户关于如何在SQL Server中使用PIVOT函数实现行转列的问题。首先,我要回忆一下PIVOT的基本概念用法。根据之前提供的引用资料,特别是引用[3],里面有PIVOT的语法结构。我需要先确保自己正确理解每个部分的作用。 用户提到想了解实现方法,所以应该分步骤说明。第一步可能需要先创建一个示例数据表,这样用户能更直观地看到转换前后的对比。比如,可以设计一个学生成绩表,包含姓名、科目分数,这样行转列后,科目会成为列名,分数作为对应的值。引用[3]中的示例用了类似的思路,可能可以参考。 接下来,我需要解释PIVOT的语法结构。根据引用[3]的批注,PIVOT需要指定非透视的列、聚合函数以及需要转换的列。这里要注意的是,除了要转换的列(如科目)外,其他列默认会被分组,可能需要使用聚合函数,比如SUM或AVG。用户可能会疑惑为什么必须用聚合函数,这时候需要说明PIVOT通常用于汇总数据,因此需要聚合。 然后,给出具体的示例代码。比如,原始数据是每个学生的各科成绩,行转列后每个学生的成绩显示在对应的科目列下。代码部分需要包括创建表的语句、插入数据,然后使用PIVOT进行转换。同时,要注释每段代码的作用,方便用户理解。 另外,引用[4]提到了UNPIVOT的示例,但用户主要关注的是行转列,所以可能不需要详细展开UNPIVOT,但可以简要提及其存在,作为逆操作。 可能还需要提到使用动态SQL的情况,因为如果列的值不固定,静态PIVOT可能不够灵活。这时候需要动态生成列名,但考虑到复杂度,可能作为进阶内容提及,并给出参考链接或建议进一步学习的资源。 在解释过程中,需要注意LaTeX格式的正确使用,比如行内公式用$...$,独立公式用$$...$$,但在这个问题中可能数学公式较少,更多的是代码示例。因此,要确保代码块正确包裹在三个反引号中,并标明语言为sql。 用户的问题还可能涉及到多个聚合函数的使用,如引用[2]中提到的,但示例可能只需要一个聚合函数,比如SUM。如果有需要,可以说明如何在一个PIVOT中使用多个聚合,但可能会比较复杂,可以建议用户参考引用[2]的资料。 最后,生成相关问题时要考虑用户可能进一步想知道的内容,比如动态行转列、性能优化、与其他方法的对比等。 现在需要检查是否有遗漏的步骤,比如数据准备、语法分解、示例结果展示等。确保引用的资料正确标注,比如引用[3]详细解释了PIVOT语法,需要在相关部分添加引用标识。同时,要避免使用Markdown格式,保持回答结构清晰,分步骤说明,并自然融入引用标注。</think>### 如何在 SQL Server 中使用 PIVOT 函数实现行转列? #### 1. **行转列的核心逻辑** 行转列(PIVOT)是将行数据中的某一列的唯一值转换为新表的列名,并将对应的数据填充到新列中。例如,将学生成绩表中的“科目”列(如语文、数学、英语)转换为独立的列[^3]。 --- #### 2. **语法结构** PIVOT 的语法分为三部分: - **非透视列**:不参与转换的列(如学生姓名),作为分组依据。 - **聚合函数**:对需要展示的数值列进行汇总(如 `SUM(分数)`)。 - **透视列**:需要转换为列名的原数据列(如科目)。 ```sql SELECT [非透视列], [透视值1], [透视值2], ... FROM ( SELECT [非透视列], [透视列], [数值列] FROM 表名 ) AS 源表 PIVOT ( <聚合函数>([数值列]) FOR [透视列] IN ([透视值1], [透视值2], ...) ) AS 透视表; ``` --- #### 3. **具体示例** ##### **步骤 1:创建测试数据** ```sql CREATE TABLE student_scores ( name VARCHAR(50), subject VARCHAR(50), score INT ); INSERT INTO student_scores VALUES ('张三', '语文', 90), ('张三', '数学', 85), ('李四', '语文', 88), ('李四', '英语', 92); ``` ##### **步骤 2:使用 PIVOT 行转列** ```sql SELECT name, [语文], [数学], [英语] FROM ( SELECT name, subject, score FROM student_scores ) AS SourceTable PIVOT ( SUM(score) -- 聚合函数:对分数求 FOR subject IN ([语文], [数学], [英语]) -- 将科目转换为列 ) AS PivotTable; ``` ##### **结果** | name | 语文 | 数学 | 英语 | |------|-----|-----|-----| | 张三 | 90 | 85 | NULL| | 李四 | 88 | NULL| 92 | --- #### 4. **注意事项** - **聚合函数必要性**:PIVOT 必须指定聚合函数(如 `SUM`, `AVG`),因为转换后的列可能对应多行数据[^3]。 - **动态列处理**:若透视列的值不固定(如科目可能新增),需使用动态 SQL 生成列名,但语法较复杂[^2]。 - **空值处理**:未匹配的数据会显示为 `NULL`,可用 `COALESCE` 函数替换为默认值。 --- #### 5. **与其他方法的对比** - **CASE WHEN**:手动编写条件语句实现行转列,灵活性高但代码冗余。 - **UNPIVOT**:逆操作(列转行),将多列合并为一列[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值