没有聚合的 sql server 中的 UNPIVOT

Posted

技术标签:

【中文标题】没有聚合的 sql server 中的 UNPIVOT【英文标题】:UNPIVOT in sql server without Aggregate 【发布时间】:2016-07-21 09:49:41 【问题描述】:

我有一个Table,如下所示。

OfficeID    SunFrom SunTo   MonFrom MonTo   TueFrom TueTo   WedFrom WedTo   ThuFrom ThuTo   FriFrom FriTo   SatFrom SatTo
51834       12      17      8       22      8       22      8       22      8       22      9       21      8       19

我需要将此表的列转换为行

预期输出:

Officeid    Day         Daystart    DayEnd

51834       Sunday      12          17
51834       Monday      8           22
51834       Tuesday     8           22
51834       Wednesday   8           22
51834       Thursday    8           22
51834       Friday      9           21
51834       Saturday    8           19

我尝试使用UNPIVOT,但我不确定我需要选择哪一列作为pivot column。请帮忙....

【问题讨论】:

我认为最好的方法是运行 7 个查询,每个查询针对一周中的一天。如果这是某种常规的事情,那么您可以创建一个存储过程或使用动态查询来执行此操作。 【参考方案1】:

这可以解决大部分问题。如果您需要特定的输出顺序,我建议使用将 3 个字符的日期名称映射到排序索引(以及全长名称,如果需要)的辅助表:

declare @t table (OfficeID int,SunFrom  int,SunTo int,MonFrom int,MonTo int,
TueFrom int,TueTo int,WedFrom int,WedTo int,
ThuFrom int,ThuTo int,FriFrom int,FriTo int,
SatFrom int,SatTo int)
insert into @t(OfficeID,SunFrom,SunTo,MonFrom,MonTo,TueFrom,TueTo,
WedFrom,WedTo,ThuFrom,ThuTo,FriFrom,FriTo,SatFrom,SatTo) values
(51834,12,17,8,22,8,22,8,22,8,22,9,21,8,19)

select
    OfficeID,Day,
        MAX(CASE WHEN Endpoint='From' THEN EndpointTime END) as Daystart,
        MAX(CASE WHEN Endpoint='To' THEN EndpointTime END) as DayEnd
from
    @t t
        unpivot
    (EndpointTime for DayAndEndPoint in (SunFrom,SunTo,MonFrom,MonTo,TueFrom,TueTo,
WedFrom,WedTo,ThuFrom,ThuTo,FriFrom,FriTo,SatFrom,SatTo)) a
        cross apply
    (select SUBSTRING(DayAndEndpoint,1,3) as Day,SUBSTRING(DayAndEndpoint,4,4) as Endpoint) b
group by
    OfficeID,Day

结果:

OfficeID    Day  Daystart    DayEnd
----------- ---- ----------- -----------
51834       Fri  9           21
51834       Mon  8           22
51834       Sat  8           19
51834       Sun  12          17
51834       Thu  8           22
51834       Tue  8           22
51834       Wed  8           22

正如您所希望的那样,如果可能的话,最好重新设计您的数据库,使其比原始结果集更接近这个结果集 - 相同“类型”的数据应该在单个列中,并且数据应该被建模为数据,而不是嵌入到列名中。


如果输出顺序和日期名称特别重要,则扩展变体:

declare @t table (OfficeID int,SunFrom  int,SunTo int,MonFrom int,MonTo int,
TueFrom int,TueTo int,WedFrom int,WedTo int,
ThuFrom int,ThuTo int,FriFrom int,FriTo int,
SatFrom int,SatTo int)
insert into @t(OfficeID,SunFrom,SunTo,MonFrom,MonTo,TueFrom,TueTo,
WedFrom,WedTo,ThuFrom,ThuTo,FriFrom,FriTo,SatFrom,SatTo) values
(51834,12,17,8,22,8,22,8,22,8,22,9,21,8,19)

declare @DayNameAndSort table (Day varchar(3) not null,ExtendedName varchar(19) not null,SortOrder int not null)
insert into @DayNameAndSort (Day,ExtendedName,SortOrder) values
('Fri','Friday',   5),
('Mon','Monday',   1),
('Sat','Saturday', 6),
('Sun','Sunday',   0),
('Thu','Thursday', 4),
('Tue','Tuesday',  2),
('Wed','Wednesday',3)


;With ReOriented as (
select
    OfficeID,Day,
        MAX(CASE WHEN Endpoint='From' THEN EndpointTime END) as Daystart,
        MAX(CASE WHEN Endpoint='To' THEN EndpointTime END) as DayEnd
from
    @t t
        unpivot
    (EndpointTime for DayAndEndPoint in (SunFrom,SunTo,MonFrom,MonTo,TueFrom,TueTo,
WedFrom,WedTo,ThuFrom,ThuTo,FriFrom,FriTo,SatFrom,SatTo)) a
        cross apply
    (select SUBSTRING(DayAndEndpoint,1,3) as Day,SUBSTRING(DayAndEndpoint,4,4) as Endpoint) b
group by
    OfficeID,Day
)
select
    OfficeID,ExtendedName,Daystart,DayEnd
from
    ReOriented r
        inner join
    @DayNameAndSort s
        on
            r.Day = s.Day
order by s.SortOrder

【讨论】:

感谢 damien..但是可以返回 day Id 而不是 dayname 吗?比如 1 代表太阳,2 代表星期一? @bmsqldev - 你应该能够调整我的第二个版本,它使用一个辅助表来引入有关日子的额外信息。

以上是关于没有聚合的 sql server 中的 UNPIVOT的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server报错:选择列表中的列无效,因为该列没有包含在聚合函数或 GROUP BY 子句中

SQL Server中的聚合函数都有哪些?

SQL Server中的聚合函数都有哪些?

SQL Server“不能对包含聚合或子查询的表达式执行聚合函数”,但 Sybase 可以

SQL Server 中的自定义聚合函数 (concat)

SQL Server 2005 中的数据聚合