(4) PIVOT 和 UPIVOT 的使用

最近項目中用到了行轉列,使用SQL SERVER 提供的PIVOT實現起來很是容易。sql

官方解釋:詳見這裏數據庫

能夠使用 PIVOT 和 UNPIVOT 關係運算符將表值表達式更改成另外一個表。ide

PIVOT 經過將表達式某一列中的惟一值轉換爲輸出中的多個列來旋轉表值表達式,並在必要時對最終輸出中所需的任何其他列值執行聚合。UNPIVOT 與 PIVOT 執行相反的操做,將表值表達式的列轉換爲列值。函數

其實PIVOT 就是行轉列,UNPIVOT就是列轉行。spa

PIVOT 的完整語法爲:3d

SELECT <非透視的列>,code

    [第一個透視的列] AS <列名稱>,blog

    [第二個透視的列] AS <列名稱>,ci

    ...get

    [最後一個透視的列] AS <列名稱>,

FROM

    (<生成數據的 SELECT 查詢>)

    AS <源查詢的別名>

PIVOT

(

    <聚合函數>(<要聚合的列>)

FOR

[<包含要成爲列標題的值的列>]

    IN ( [第一個透視的列], [第二個透視的列],

    ... [最後一個透視的列])

) AS <透視表的別名>

<可選的 ORDER BY 子句>;

UNPIVOT的完整語法相對簡單一些爲:

SELECT <其餘列>,<虛擬列別名>,<列值別名>

UNPIVOT(

<列值別名>

FOR <虛擬列別名>

IN(<第一個真實列>,<第二個真實列>....)

) AS <表別名>

咱們來看一個簡單PIVOT的例子,項目有以下要求:根據用戶輸入的查詢月份,統計全部設備房間此月的告警次數,界面報表要求以下格式:

設備房間 告警A次數 告警B次數 告警C次數
XXX 10 1 2
ZZZ 1 0 5





例如:數據庫中有以下表和數據:

--機房表
create table t_DevRoom
( 
  RoomId int identity(1,1),
  RoomName nvarchar(50),
  constraint  [Pk_DevRoom_RoomId] primary key clustered(RoomId),
  constraint  [Uq_DevRoom_RoomName] unique (RoomName)
)
go

--告警類型
create table t_AlarmType
(
  TypeId int,
  TypeName nvarchar(20) not null,
  constraint  [Pk_AlarmType_TypeId] primary key clustered(TypeId),
  constraint  [Uq_AlarmType_TypeName] unique (TypeName)
)
go

--告警表
create table t_Alarm 
(
  AlarmId int identity(1,1),
  RoomId int not null,
  AlarmType int not null,  
  AlarmDt datetime not null,
  constraint [Pk_Alarm_AlarmId] primary key clustered(AlarmId),
  constraint [Fk_Alarm_RoomId] foreign key (RoomId) references t_DevRoom(RoomId) on delete cascade,
  constraint [Fk_Alarm_AlarmType] foreign key (AlarmType) references t_AlarmType(TypeId) on delete cascade
)
go

insert into t_DevRoom values ('機房A')
insert into t_DevRoom values ('機房B')
insert into t_DevRoom values ('機房C')

insert into t_AlarmType values (1,'空調告警')
insert into t_AlarmType values (2,'煙霧告警')
insert into t_AlarmType values (3,'設備告警')

insert into t_Alarm values(1,1,'2013-01-01')
insert into t_Alarm values(1,1,'2013-01-02')
insert into t_Alarm values(1,2,'2013-01-02')
insert into t_Alarm values(1,3,'2013-01-03')
insert into t_Alarm values(1,3,'2013-01-04')

insert into t_Alarm values(2,2,'2013-01-01')
insert into t_Alarm values(2,2,'2013-01-02')
insert into t_Alarm values(2,3,'2013-01-02')
insert into t_Alarm values(2,3,'2013-01-03')
insert into t_Alarm values(2,3,'2013-01-04')

有了上面的臨時數據,咱們能夠查詢一下2013年1月份全部機房的告警次數:

select R.RoomId,R.RoomName,count(A.AlarmType) as nums,T.TypeName from t_DevRoom as R
left join t_Alarm as A on R.RoomId=A.RoomId
left join t_AlarmType AS T on A.AlarmType=T.TypeId
WHERE datepart(year,A.AlarmDt)=2013 AND datepart(month,A.AlarmDt)=1 or A.AlarmDt is null
group by R.RoomId,R.RoomName,T.TypeName
order by RoomId

結果以下:

咱們來把這個結果集PIVOT一下,以符合咱們的界面要求,咱們根據語法格式進行這樣修改:

select RoomId,RoomName,
       alarm_kt=isnull([空調告警],0),
       alarm_yw=isnull([煙霧告警],0),
       alarm_dv=isnull([設備告警],0)
from
(
select R.RoomId,R.RoomName,T.TypeName,count(A.AlarmType) as nums from t_DevRoom as R
left join t_Alarm as A on R.RoomId=A.RoomId
left join t_AlarmType AS T on A.AlarmType=T.TypeId
WHERE datepart(year,A.AlarmDt)=2013 AND datepart(month,A.AlarmDt)=1 or A.AlarmDt is null
group by R.RoomId,R.RoomName,T.TypeName
) as temp
pivot
(
   min(nums) for TypeName IN([空調告警],[煙霧告警],[設備告警])
) as temp2
order by RoomId

查詢結果以下:

至於 UNPIVOT 與PIVOT正好相反,也來看個例子,此例子來自於網上:

create table t_score
(
  姓名 varchar(10),
  語文 int,
  數學 int,
  物理 int
)
go

insert into t_score values('張三',74,83,93)
insert into t_score values('李四',74,84,94)


select * from t_score
select 姓名,課程,分數 from t_score unpivot ( 分數 for 課程 in([語文],[數學],[物理]) ) as t go

執行結果以下:

相關文章
相關標籤/搜索