PIVOT(透視轉換)和UNPIVOT(逆透視轉換)


一、原數據狀態

二、手動寫透視轉換1

三、手動寫透視轉換2

四、PIVOT(透視轉換)和UNPIVOT(逆透視轉換)詳細使用

  • 使用標准SQL進行透視轉換和逆視轉換
--行列轉換
create table #demoOrders
(
   id int primary key identity(1,1),
   CompanyName nvarchar(50),
   ProductID int,
   ProductName nvarchar(50)
)
insert into #demoOrders (CompanyName,ProductID,ProductName) values('公司1','1','產品1')
insert into #demoOrders (CompanyName,ProductID,ProductName) values('公司1','2','產品2')
insert into #demoOrders (CompanyName,ProductID,ProductName) values('公司2','2','產品2')
insert into #demoOrders (CompanyName,ProductID,ProductName) values('公司2','3','產品3')
insert into #demoOrders (CompanyName,ProductID,ProductName) values('公司3','3','產品3')
insert into #demoOrders (CompanyName,ProductID,ProductName) values('公司4','3','產品3')
insert into #demoOrders (CompanyName,ProductID,ProductName) values('公司5','4','產品4')
insert into #demoOrders (CompanyName,ProductID,ProductName) values('公司6','4','產品4')
insert into #demoOrders (CompanyName,ProductID,ProductName) values('公司6','5','產品5')

select * from #demoOrders

  

  透視轉換的標准SQL解決方案以一種非常直接的方式來處理轉換過程中涉及的三個階段:
    1、分組階段用group by 子句實現
    2、擴展階段通過在select子句中為每個目標列指定case表達式來實現,這需要事先知道每個擴展元素的取值,並為每個值指定一個單獨的case表達式。
    3、聚合階段通過為每個case表達式的結果應用相關的聚合函數來實現。

  解題思維步驟:

  1.先找到為行列轉換的數據,分組查看數據試試: 

select CompanyName,ProductName,count(*) as num from #demoOrders
group by ProductName,CompanyName order by CompanyName

  

  2.分組階段:用group by 子句以行作為分組條件,獲取行數據

select CompanyName
from (
    select CompanyName,ProductName,COUNT(*)as num from #demoOrders group by ProductName,CompanyName
) T 
group by CompanyName

  

  3.擴展階段:找到列的數據,為每個目標列指定case表達式;聚合階段通過為每個case表達式的結果應用相關的聚合函數來實現

select CompanyName,
sum(case when ProductName='產品1' then num else 0 end)[產品1],
sum(case when ProductName='產品2' then num else 0 end)[產品2],
sum(case when ProductName='產品3' then num else 0 end)[產品3],
sum(case when ProductName='產品4' then num else 0 end)[產品4],
sum(case when ProductName='產品5' then num else 0 end)[產品5] 
from (
    select CompanyName,ProductName,COUNT(*)as num from #demoOrders group by ProductName,CompanyName
) T 
group by CompanyName

--以下是分頁存儲過程,看看拼接sql語句字符串和執行的過程,然后把思路打開一下試試
declare @sql nvarchar(1000)
set @sql='select CompanyName,'--開始設置語句
--------動態生成語句begin(開始轉成列)-----
select @sql=@sql+'sum(case when ProductName='''+ProductName+''' then num else 0 end)['+ProductName+'],'
from (select distinct top 100 percent ProductName from #demoOrders order by ProductName)a
--------動態生成語句  end--------------------
print @sql
set @sql =left(@sql,len(@sql)-1)+' from (select CompanyName,ProductName,COUNT(*)as num   from #demoOrders group by ProductName,CompanyName)a group by CompanyName'
print @sql    --打印輸出最終執行的SQL
exec(@sql)    --執行SQL字符串

   

  

  逆透視轉換的標准SQL解決方案要實現三個邏輯處理階段:
    1、生成副本:根據來源表的每一行生成多個副本(為需要逆透視的每個列生成一個副本);用cross join(交叉聯接)來生成每一行的多個副本
    2、提取元素
    3、刪除不相關的交叉

--逆視數據
select CompanyName,
sum(case when ProductName='產品1' then num else 0 end)[產品1],
sum(case when ProductName='產品2' then num else 0 end)[產品2],
sum(case when ProductName='產品3' then num else 0 end)[產品3],
sum(case when ProductName='產品4' then num else 0 end)[產品4],
sum(case when ProductName='產品5' then num else 0 end)[產品5] 
into #unpivotDemo
from (
    select CompanyName,ProductName,COUNT(*)as num from #demoOrders group by ProductName,CompanyName
) a group by CompanyName

  1、在#unpivotDemo表和每行ProductName之間進行交叉聯接

select * from #unpivotDemo 
cross join 
(values('產品1'),('產品2'),('產品3'),('產品4'),('產品5')) as #unpivotDemo2(ProductName)
--或:
select * from #unpivotDemo 
cross join 
(
    select '產品1' as ProductName 
    union all
    select '產品2'
    union all
    select '產品3'
    union all
    select '產品4'
    union all
    select '產品5'
) as #unpivotDemo2

  

  2.1、生成一個數據列,由它返回與當前副本所代表的產品相對應的列值  

select *,
case ProductName
    when '產品1' then 產品1
    when '產品2' then 產品2
    when '產品3' then 產品3
    when '產品4' then 產品4
    when '產品5' then 產品5
end as num
from #unpivotDemo 
cross join (values('產品1'),('產品2'),('產品3'),('產品4'),('產品5')) as #unpivotDemo2(ProductName)
--或:
select *,
case ProductName
    when '產品1' then 產品1
    when '產品2' then 產品2
    when '產品3' then 產品3
    when '產品4' then 產品4
    when '產品5' then 產品5
end as num
from #unpivotDemo 
cross join 
(
    select '產品1' as ProductName 
    union all
    select '產品2'
    union all
    select '產品3'
    union all
    select '產品4'
    union all
    select '產品5'
) as #unpivotDemo2

  

  2.2、提取所需的數據列  

select CompanyName,ProductName,
case ProductName
    when '產品1' then 產品1
    when '產品2' then 產品2
    when '產品3' then 產品3
    when '產品4' then 產品4
    when '產品5' then 產品5
end as num
from #unpivotDemo 
cross join (values('產品1'),('產品2'),('產品3'),('產品4'),('產品5')) as #unpivotDemo2(ProductName)
--或:
select CompanyName,ProductName,
case ProductName
    when '產品1' then 產品1
    when '產品2' then 產品2
    when '產品3' then 產品3
    when '產品4' then 產品4
    when '產品5' then 產品5
end as num
from #unpivotDemo 
cross join 
(
    select '產品1' as ProductName 
    union all
    select '產品2'
    union all
    select '產品3'
    union all
    select '產品4'
    union all
    select '產品5'
) as #unpivotDemo2

  

  3、0值與NULL值代表不相關的交叉,為了刪除不相關的交叉,在外部查詢中過濾掉0值與NULL值

select * from
(
    select CompanyName,ProductName,
    case ProductName
        when '產品1' then 產品1
        when '產品2' then 產品2
        when '產品3' then 產品3
        when '產品4' then 產品4
        when '產品5' then 產品5
    end as num
    from #unpivotDemo 
    cross join (values('產品1'),('產品2'),('產品3'),('產品4'),('產品5')) as #unpivotDemo2(ProductName)
) as T
where num is not null and num <> 0
--或:
select * from
(
    select CompanyName,ProductName,
    case ProductName
        when '產品1' then 產品1
        when '產品2' then 產品2
        when '產品3' then 產品3
        when '產品4' then 產品4
        when '產品5' then 產品5
    end as num
    from #unpivotDemo 
    cross join 
    (
        select '產品1' as ProductName 
        union all
        select '產品2'
        union all
        select '產品3'
        union all
        select '產品4'
        union all
        select '產品5'
    ) as #unpivotDemo2
) as T
where num is not null and num <> 0

  

  • 使用T-SQL PIVOT透視轉換和UNPIVOT逆透視轉換

  pivot的使用

select CompanyName,[產品1] as 產品1,[產品2] as 產品2,[產品3] as 產品3,[產品4] as 產品4,[產品5] as 產品5
from 
(
    --表表達式作為pivot輸入表,僅僅返回透視中用到的列
    select CompanyName,ProductName,count(*) as num from #demoOrders
    group by ProductName,CompanyName
) as sourceTable    --分組是隱含的,對表中除掉聚合和條件的列進行分組
pivot
(
    sum(num)    --聚合函數
    for ProductName in([產品1],[產品2],[產品3],[產品4],[產品5])    --准備做列名
) as PivotTable

 

create table #demotable
(
   id int primary key identity(1,1),
   orderMonth int ,
   subTotal decimal(18,2)
)
insert into #demotable (orderMonth,subTotal) values(5,100.00)
insert into #demotable (orderMonth,subTotal) values(6,100.00)
insert into #demotable (orderMonth,subTotal) values(5,200.00)
insert into #demotable (orderMonth,subTotal) values(6,200.00)
insert into #demotable (orderMonth,subTotal) values(7,100.00)
select * from #demotable

--方式一
select id,[5] as 五月,[6] as 六月,[7] as 七月
from 
#demotable    --基礎表作為pivot輸入表
pivot
(
    sum(#demotable.subTotal) for #demotable.orderMonth in([5],[6],[7])
) as PivotTable
--方式二(推薦使用表表達式作為pivot的輸入表,不要對基礎表進行操作):
select id,[5] as 五月,[6] as 六月,[7] as 七月
from 
(
    --表表達式作為pivot輸入表,僅僅返回透視中用到的列
    select id,orderMonth,subTotal from #demotable
) as sourceTable    --分組是隱含的,對表中除掉聚合和條件的列進行分組
pivot
(
    sum(subTotal)    --聚合函數
    for orderMonth in([5],[6],[7])    --准備做列名
) as PivotTable
drop table #demotable

  

  unpivot的使用

create table #demotable2 
(
    id int,
    五月 int,
    六月 int,
    七月 int
)
insert into #demotable2 values (1,100,100,0);
insert into #demotable2 values (2,200,200,200);
insert into #demotable2 values (3,800,0,0);
select * from #demotable2

--執行UNPIVOT
select id,orderMonth,subTotal
FROM 
#demotable2
unpivot
(
    subTotal for orderMonth in(五月,六月,七月)
)AS UnpivotTable
drop table #demotable2

  

練習:

 

create table #testtable
(
   id int primary key identity(1,1),
   t_year int ,
   t_month int,
   t_amount decimal(18,1)
)

insert into #testtable (t_year,t_month,t_amount) values(1991,1,1.1)
insert into #testtable (t_year,t_month,t_amount) values(1991,2,1.2)
insert into #testtable (t_year,t_month,t_amount) values(1991,3,1.3)

insert into #testtable (t_year,t_month,t_amount) values(1992,1,2.1)
insert into #testtable (t_year,t_month,t_amount) values(1992,2,2.2)
insert into #testtable (t_year,t_month,t_amount) values(1992,3,2.3)
--drop table #testtable
select * from #testtable

--//想要的結果
--year m1   m2   m3
--1991 1.1  1.2  1.3
--1992 2.1  2.2  2.3

select max(t_year) as [year],max([1]) as m1,max([2]) as m2,max([3]) as m3
from #testtable
pivot 
(
  max(t_amount) for t_month in([1],[2],[3])
) as PivotTable
group by t_year

select t_amount,ColumnName,YearAndMonth
from #testtable
unpivot
(
    YearAndMonth for ColumnName in(t_year,t_month)
) as UnpivotTable

--行列轉換
--解題思維步驟:
--1.先找到為行列轉換的數據,查看數據試試:
select t_year,t_month,t_amount from #testtable
--2.找到列的數據
select
(case when t_month=1 then t_amount else 0 end)[m1],
(case when t_month=2 then t_amount else 0 end)[m2],
(case when t_month=3 then t_amount else 0 end)[m3]
from #testtable
--3.以行作為分組條件,獲取行數據;兩者結合起來,答案:
select t_year,
max(case when t_month=1 then t_amount else 0 end)[m1],
max(case when t_month=2 then t_amount else 0 end)[m2],
max(case when t_month=3 then t_amount else 0 end)[m3]
from #testtable
group by t_year

--------------------以下是sql語句字符串和執行的過程------------------------
declare @sql nvarchar(1000)
set @sql='select t_year,'
--------動態生成列 begin--------
select @sql=@sql+'max(case when t_month='+convert(nvarchar(20),t_month)+' then t_amount else 0 end)[m'+str(t_month,1)+'],'
from (select distinct top 100 percent t_month from #testtable order by t_month) T
print @sql
--------動態生成列 end--------
set @sql=left(@sql,len(@sql)-1)+' from #testtable group by t_year'
print @sql
exec(@sql)

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM