在 SQL Server 2008 中将行的显示更改为单行

use*_*979 1 sql-server-2008 pivot

我创建了一个包含以下 2 个小表的数据库

这是数据库脚本

USE [Test1July]
GO
/****** Object:  Table [dbo].[Hotels]    Script Date: 07/01/2014 22:33:33 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Hotels](
    [SeqID] [int] IDENTITY(1,1) NOT NULL,
    [HotelName] [nchar](25) NULL,
 CONSTRAINT [PK_Hotels] PRIMARY KEY CLUSTERED 
(
    [SeqID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object:  Table [dbo].[HotelRooms]    Script Date: 07/01/2014 22:33:33 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[HotelRooms](
    [SeqID] [int] IDENTITY(1,1) NOT NULL,
    [HotelSeqID] [int] NOT NULL,
    [FloorNo] [int] NOT NULL,
    [RoomNo] [int] NOT NULL,
    [Beds] [int] NOT NULL
) ON [PRIMARY]
GO
/****** Object:  StoredProcedure [dbo].[GetFloorStructure]    Script Date: 07/01/2014 22:33:34 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE  PROCEDURE [dbo].[GetFloorStructure](

@HotelSeqID INT

)
AS
BEGIN
SELECT     HotelRooms.FloorNo, HotelRooms.RoomNo, HotelRooms.Beds
FROM         HotelRooms INNER JOIN
                      Hotels ON HotelRooms.HotelSeqID = Hotels.SeqID
WHERE     (HotelRooms.HotelSeqID = @HotelSeqID)
END
GO
/****** Object:  ForeignKey [FK_HotelRooms_Hotels]    Script Date: 07/01/2014 22:33:33 ******/
ALTER TABLE [dbo].[HotelRooms]  WITH CHECK ADD  CONSTRAINT [FK_HotelRooms_Hotels] FOREIGN KEY([HotelSeqID])
REFERENCES [dbo].[Hotels] ([SeqID])
GO
ALTER TABLE [dbo].[HotelRooms] CHECK CONSTRAINT [FK_HotelRooms_Hotels]
GO
Run Code Online (Sandbox Code Playgroud)

我正在寻找一种解决方案,显示酒店楼层的楼层结构,显示房间数据,如 roomNo 和床位,而不是垂直 - 即我应该能够查看特定数据的 1 楼的单行数据。我一直在尝试使用动态 PIVOT,但我对 PIVOT 的了解非常有限。

我设法创建了一个动态 PIVOT 查询,但没有得到预期的结果。请建议如何在动态生成的列中实现单行中的多行结果 - 这是我的查询

DECLARE @colsPivot AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @colsPivot = STUFF((SELECT ',' + QUOTENAME(c.col + '_'+cast(rn as varchar(10))) 
                    from
                    (
                      select row_number() over(partition by FloorNo, RoomNo
                                                                    order by Beds) rn
                      from HotelRooms 
                    ) t
                    cross apply
                    (
                      select 'Beds' col, 1 so union all
                      select 'HotelSeqID', 2
                    ) c
                    group by col, rn, so
                    order by rn, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query 
  = 'select FloorNo, RoomNo,  '+@colsPivot+' 
      from
      (
        select FloorNo, RoomNo, 
          col+''_''+cast(rn as varchar(10)) col, 
          val
        from 
        (
          select FloorNo, RoomNo, Beds, HotelSeqID
            , row_number() over(partition by FloorNo, RoomNo
                                order by Beds) rn
          from HotelRooms
        ) d
        unpivot
        (
          val
          for col in (Beds, HotelSeqID)
        ) un
      ) s
      pivot
      (
        max(val)
        for col in ('+ @colspivot +')
      ) p'

exec(@query);
Run Code Online (Sandbox Code Playgroud)

这是我从一个简单的选择查询中得到的输出

FloorNo     RoomNo      Beds
1           101         1
1           102         2
1           103         1
1           104         2
2           201         1
2           202         2
2           203         1
2           204         2
2           205         1
2           206         2
Run Code Online (Sandbox Code Playgroud)

但我想要这种格式的输出

FloorNo     RoomNo      Beds     RoomNo      Beds     RoomNo      Beds 
1           101         1        102         2        103         1
Run Code Online (Sandbox Code Playgroud)

这是查询的 STATIC 版本,它为我提供了所需的结果

select HotelSeqID, FloorNo,
  max(case when rn = 1 then RoomNo  end) RoomNo,
  max(case when rn = 1 then Beds  end) Beds,

  max(case when rn = 2 then RoomNo  end) RoomNo,
  max(case when rn = 2 then Beds  end) Beds,

  max(case when rn = 3 then RoomNo  end) RoomNo,
  max(case when rn = 3 then Beds  end) Beds,

  max(case when rn = 4 then RoomNo  end) RoomNo,
  max(case when rn = 4 then Beds  end) Beds,

  max(case when rn = 5 then RoomNo  end) RoomNo,
  max(case when rn = 5 then Beds  end) Beds 

from
(
  select HotelSeqID, FloorNo, RoomNo, Beds,
    row_number() over(partition by HotelSeqID ORDER BY FloorNo) rn
  from HotelRooms 
  WHERE     (HotelRooms.HotelSeqID = @HotelSeqID)
) src

group by HotelSeqID, FloorNo;
Run Code Online (Sandbox Code Playgroud)

我只需要这个查询的动态版本 -

Tar*_*ryn 5

由于您想使用它PIVOT来获取结果并且需要动态地获取结果,因此我始终建议先编写静态 PIVOT 查询,这样您就可以在尝试将其转换为动态 SQL 之前获得正确的语法。

所需的输出显示,要在两列PIVOT,RoomNoBeds-作为一个结果,你需要先UNPIVOT这些列,然后应用的枢纽。

您当前的查询是在正确的轨道上,您确实需要使用,row_number()以便您可以获得每层楼的房间/床位数量 - 但您的逆枢轴正在使用BedsHotelSeqID。您不想取消透视,HotelSeqID因为它没有您最终想要作为新列的值。

我会通过以下方式启动一个静态版本 - 首先是子查询从你的表中垂直获取数据,row_number()包括:

select h.HotelName, 
  r.FloorNo,
  r.RoomNo,
  r.Beds,
  seq = row_number() over(partition by h.HotelName, r.FloorNo
                          order by r.RoomNo) 
from dbo.Hotels h
inner join dbo.HotelRooms r
  on h.seqid = r.hotelseqid
Run Code Online (Sandbox Code Playgroud)

请参阅SQL Fiddle with Demo。您的数据将如下所示,其中包含基于HotelName和的序列号的新列FloorNo

|                 HOTELNAME | FLOORNO | ROOMNO | BEDS | SEQ |
|---------------------------|---------|--------|------|-----|
| Hotel 1                   |       1 |    101 |    1 |   1 |
| Hotel 1                   |       1 |    102 |    2 |   2 |
| Hotel 1                   |       1 |    103 |    1 |   3 |
| Hotel 1                   |       1 |    104 |    2 |   4 |
| Hotel 1                   |       2 |    201 |    1 |   1 |
| Hotel 1                   |       2 |    202 |    2 |   2 |
Run Code Online (Sandbox Code Playgroud)

现在,您可以将RoomNoBeds列反透视为多行。由于您使用的是 SQL Server 2008,因此您可以使用它CROSS APPLY来获取结果。查询将是:

select hr.HotelName, 
  hr.FloorNo, 
  col = c.col + '_' + cast(seq as varchar(2)),
  c.val
from
(
  select h.HotelName, 
    r.FloorNo,
    r.RoomNo,
    r.Beds,
    seq = row_number() over(partition by h.HotelName, r.FloorNo
                            order by r.RoomNo) 
  from dbo.Hotels h
  inner join dbo.HotelRooms r
    on h.seqid = r.hotelseqid
) hr
cross apply
(
  select 'RoomNo', RoomNo union all
  select 'Beds', Beds
) c (col, val);
Run Code Online (Sandbox Code Playgroud)

请参阅SQL Fiddle with Demo。您的数据现已转换为多列:

|                 HOTELNAME | FLOORNO |      COL | VAL |
|---------------------------|---------|----------|-----|
| Hotel 1                   |       1 | RoomNo_1 | 101 |
| Hotel 1                   |       1 |   Beds_1 |   1 |
| Hotel 1                   |       1 | RoomNo_2 | 102 |
| Hotel 1                   |       1 |   Beds_2 |   2 |
| Hotel 1                   |       1 | RoomNo_3 | 103 |
| Hotel 1                   |       1 |   Beds_3 |   1 |
| Hotel 1                   |       1 | RoomNo_4 | 104 |
| Hotel 1                   |       1 |   Beds_4 |   2 |
| Hotel 1                   |       2 | RoomNo_1 | 201 |
Run Code Online (Sandbox Code Playgroud)

最后,您可以旋转以获得最终结果。

select HotelName, FloorNo,
  RoomNo_1, Beds_1, RoomNo_2, Beds_2,
  RoomNo_3, Beds_3, RoomNo_4, Beds_4
from
(
  select hr.HotelName, 
    hr.FloorNo, 
    col = c.col + '_' + cast(seq as varchar(2)),
    c.val
  from
  (
    select h.HotelName, 
      r.FloorNo,
      r.RoomNo,
      r.Beds,
      seq = row_number() over(partition by h.HotelName, r.FloorNo
                              order by r.RoomNo) 
    from dbo.Hotels h
    inner join dbo.HotelRooms r
      on h.seqid = r.hotelseqid
  ) hr
  cross apply
  (
    select 'RoomNo', RoomNo union all
    select 'Beds', Beds
  ) c (col, val)
) d
pivot
(
  max(val)
  for col in (RoomNo_1, Beds_1, RoomNo_2, Beds_2,
              RoomNo_3, Beds_3, RoomNo_4, Beds_4)
) piv
order by HotelName, FloorNo;
Run Code Online (Sandbox Code Playgroud)

请参阅SQL Fiddle with Demo。一旦您测试了静态版本以确保它获得您想要的结果,您就可以轻松地将其转换为动态 SQL。

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME(col + '_' + cast(seq as varchar(2))) 
                    from
                    (
                      select seq = row_number() over(partition by h.HotelName, r.FloorNo
                                                      order by r.RoomNo) 
                      from dbo.Hotels h
                      inner join dbo.HotelRooms r
                        on h.seqid = r.hotelseqid
                    ) d
                    cross apply
                    (
                      select 'RoomNo', 1 union all
                      select 'Beds', 2
                    ) c (col, so)
                    group by col, so, seq
                    order by seq, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query = N'SELECT HotelName, FloorNo,' + @cols + N' 
            from 
            (
              select hr.HotelName, 
                hr.FloorNo, 
                col = c.col + ''_'' + cast(seq as varchar(2)),
                c.val
              from
              (
                select h.HotelName, 
                  r.FloorNo,
                  r.RoomNo,
                  r.Beds,
                  seq = row_number() over(partition by h.HotelName, r.FloorNo
                                          order by r.RoomNo) 
                from dbo.Hotels h
                inner join dbo.HotelRooms r
                  on h.seqid = r.hotelseqid
              ) hr
              cross apply
              (
                select ''RoomNo'', RoomNo union all
                select ''Beds'', Beds
              ) c (col, val)
            ) x
            pivot 
            (
                max(val)
                for col in (' + @cols + N')
            ) p 
            order by HotelName, FloorNo'

exec sp_executesql @query
Run Code Online (Sandbox Code Playgroud)

请参阅SQL Fiddle with Demo。此查询将为您提供结果:

|                 HOTELNAME | FLOORNO  |  ROOMNO_1 | BEDS_1 | ROOMNO_2 | BEDS_2 | ROOMNO_3 | BEDS_3 | ROOMNO_4 | BEDS_4 | ROOMNO_5 | BEDS_5 | ROOMNO_6 | BEDS_6 |
|---------------------------|----------|-----------|--------|----------|--------|----------|--------|----------|--------|----------|--------|----------|--------|
| Hotel 1                   |        1 |       101 |     1  |      102 |      2 |      103 |      1 |      104 |      2 |   (null) | (null) |   (null) | (null) |
| Hotel 1                   |        2 |       201 |     1  |      202 |      2 |      203 |      1 |      204 |      2 |      205 |      1 |      206 |      2 |
| Hotel 2                   |        1 |       101 |     4  |      102 |      6 |   (null) | (null) |   (null) | (null) |   (null) | (null) |   (null) | (null) |
| Hotel 2                   |        2 |       201 |     2  |      202 |      7 |   (null) | (null) |   (null) | (null) |   (null) | (null) |   (null) | (null) |
Run Code Online (Sandbox Code Playgroud)

  • @ user41979 这是一个值得一书章节的答案,而不是提示。如果它有效并且您对它感到满意,您可能想要使用箭头和它附近的标记符号。关于枢轴,您很可能不会在 SE 网络上得到更好的答案。 (2认同)
  • @user41979 请不要编辑此答案来提出另一个问题,也不要编辑您现有的问题。如果您还有其他问题,请提出一个新问题,其中包含回答所需的详细信息。 (2认同)