将日期排成列的行 - SQL Server 2008

时间:2014-02-05 14:10:04

标签: sql-server-2008 stored-procedures pivot dynamic-sql

我需要一些PIVOT表格的帮助,或者以我需要的方式得到结果。

我有一张这样的桌子。

+---------------+--------------+----------+-----------+---------------+-----------------------------------------+-----------------+---------------+------------------+----------+-------+------------+--------------+------------+-------------------+----------------+----------+-------------------+
|     Client_Id |  Project_Id  | Hotel_Id | Room_Type | Room_Category |             Allotment_Date              | Number_Of_Rooms | Number_Booked | Number_Available | Overbook | Price | Dep_Amount | Full_Payment | Admin_Only | HotelAllotment_Id | Price_Excl_VAT | VAT_Code | Charge_Dep_Amount |
+---------------+--------------+----------+-----------+---------------+-----------------------------------------+-----------------+---------------+------------------+----------+-------+------------+--------------+------------+-------------------+----------------+----------+-------------------+
|     DEFAULT   |     TEMPLATE |     2423 |       276 |               |                     2010-12-05 00:00:00 |           99999 |             1 |            99998 |        0 | 33000 |          0 |            1 |          0 |            279611 |              0 |          |                 0 |
|     DEFAULT   |     TEMPLATE |     2423 |       276 |               |                     2010-12-06 00:00:00 |           99999 |             1 |            99998 |        0 | 33000 |          0 |            1 |          0 |            279612 |              0 |          |                 0 |
|     DEFAULT   |     TEMPLATE |     2423 |       276 |               |                     2010-12-07 00:00:00 |           99999 |             1 |            99998 |        0 | 33000 |          0 |            1 |          0 |            279613 |              0 |          |                 0 |
|     DEFAULT   |     TEMPLATE |     2424 |       276 |               |                     2010-12-05 00:00:00 |           99999 |             1 |            99998 |        0 | 22000 |      22000 |            0 |          0 |            279590 |              0 |          |                 0 |
|     DEFAULT   |     TEMPLATE |     2424 |       276 |               |                     2010-12-06 00:00:00 |           99999 |             1 |            99998 |        0 | 22000 |      22000 |            0 |          0 |            279591 |              0 |          |                 0 |
|     DEFAULT   |     TEMPLATE |     2424 |       276 |               |                     2010-12-07 00:00:00 |           99999 |             1 |            99998 |        0 | 22000 |      22000 |            0 |          0 |            279592 |              0 |          |                 0 |
+---------------+--------------+----------+-----------+---------------+-----------------------------------------+-----------------+---------------+------------------+----------+-------+------------+--------------+------------+-------------------+----------------+----------+-------------------+

我需要像这样显示数据,以获取列中的日期和每天预订的数量。

+---------------+--------------+----------+-----------+---------------+------------+------------+--------------+
|     Client_Id |  Project_Id  | Hotel_Id | Room_Type | Room_Category | 2010-12-05 | 2010-12-06 | 2010-12-07   |
+---------------+--------------+----------+-----------+---------------+------------+------------+--------------+
|     DEFAULT   |     TEMPLATE |     2423 |       276 |               |          1 |          1 |            1 |
|     DEFAULT   |     TEMPLATE |     2424 |       276 |               |          1 |          1 |            1 |
+---------------+--------------+----------+-----------+---------------+------------+------------+--------------+

我需要按照Hotel_Id,Room_Type和Room_Category(如果有的话)进行分组

我需要这个是动态的,因为日期可以改变。

我尝试使用简单的数据透视表而没有运气。

任何帮助都会很棒。

2 个答案:

答案 0 :(得分:7)

您可以使用PIVOT函数在编写动态版本之前获取结果我建议先编写静态版本。

如果您的值有限,则可以将所有dates硬编码为列标题:

select client_id, project_id, hotel_id,
  room_type, room_category,
  [2010-12-05], [2010-12-06], [2010-12-07]
from 
(
  select client_id, project_id, hotel_id,
    room_type, room_category,
    allotment_date, number_booked
  from yourtable
) d
pivot
(
  sum(number_booked)
  for allotment_date in ([2010-12-05], [2010-12-06], [2010-12-07])
) p;

SQL Fiddle with Demo。但是如果您的值未知,那么您将需要创建一个使用动态SQL执行的sql字符串。

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

select @cols = STUFF((SELECT ',' + QUOTENAME(convert(varchar(10), allotment_date, 120)) 
                    from yourtable
                    group by allotment_date
                    order by allotment_date
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT client_id, project_id, hotel_id,
                room_type, room_category,' + @cols + ' 
            from 
            (
              select client_id, project_id, hotel_id,
                room_type, room_category,
                allotment_date, number_booked
              from yourtable
            ) x
            pivot 
            (
                sum(number_booked)
                for allotment_date in (' + @cols + ')
            ) p '

execute sp_executesql @query;

SQL Fiddle with Demo。结果如下:

| CLIENT_ID | PROJECT_ID | HOTEL_ID | ROOM_TYPE | ROOM_CATEGORY | 2010-12-05 | 2010-12-06 | 2010-12-07 |
|-----------|------------|----------|-----------|---------------|------------|------------|------------|
|   DEFAULT |   TEMPLATE |     2423 |       276 |        (null) |          1 |          1 |          1 |
|   DEFAULT |   TEMPLATE |     2424 |       276 |        (null) |          1 |          1 |          1 |

答案 1 :(得分:2)

编辑:关于此“解决方案”的进一步讨论表明,进一步详细说明为什么这不应该被用作可靠性是明智的码。在SELECT *之类的明显的永恒之类之外,沿着这条路走下去只会超过编译时间限制。假设我们使用以下(以及更好的方式)CTE填充@t_Date表变量:

    -- Thanks @AaronBertrand!
    WITH cte_Date(DateVal) AS (
        SELECT TOP (10000) DATEADD(DAY, ROW_NUMBER() 
                   OVER (ORDER BY s1.object_id), '19991231')
        FROM sys.all_objects AS s1
        CROSS JOIN sys.all_objects AS s2
    )
    INSERT INTO @t_Date ( DateVal )
    SELECT  DateVal
    FROM    cte_Date;

不可避免地,我们会在某些时候遇到如下消息:

  

Msg 8632,Level 17,State 2,Line 2

     

内部错误:已达到表达式服务限制。请在查询中查找可能复杂的表达式,并尝试简化它们。

请注意,即使是错误消息也可以汇总,因为@bluefeet和@Lamak或多或少都说过,“不要在数据层中这样做。”

所以,帖子是:

@bluefeet和@Lamak是StackOverflow上两个最高级别的数据库人员,所以你会想要留意他们的话。如果确实需要使用@ bluefeet的答案进行测试,因为您收到编译时错误,您可以通过以下示例来解决问题并解决这个问题。请为了任何可能需要接管工作职责的人,不要将这样的代码移到生产环境中。我发布了这个hack-fest解决方法,以便您可以测试您正在处理的任何理论,并通过适当的步骤进行跟进,以使您的数据层更适合部署。

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'yourtable'
                    AND type = 'U' )
BEGIN
    --DROP TABLE dbo.yourtable;
    CREATE TABLE dbo.yourtable
    (    
        [Client_Id] varchar(7), 
        [Project_Id] varchar(8), 
        [Hotel_Id] int, 
        [Room_Type] int, 
        [Room_Category] int, 
        [Allotment_Date] datetime, 
        [Number_Of_Rooms] int, 
        [Number_Booked] int, 
        [Number_Available] int, 
        [Overbook] int, 
        [Price] int, 
        [Dep_Amount] int, 
        [Full_Payment] int, 
        [Admin_Only] int, 
        [HotelAllotment_Id] int, 
        [Price_Excl_VAT] int, 
        [VAT_Code] int, 
        [Charge_Dep_Amount] INT
    );

    DECLARE @t_Date    TABLE
    (
        DateVal        DATE
    );

    WITH cte_Date AS (
        SELECT  DateVal = CAST( GETDATE() AS DATE )
        UNION ALL
        SELECT  DateVal = DATEADD( DAY, -1, DateVal )
        FROM    cte_Date
        WHERE   DateVal > '2002-01-01'  
    )
    INSERT INTO @t_Date ( DateVal )
    SELECT  DateVal
    FROM    cte_Date
    OPTION ( MAXRECURSION 5000 );

    INSERT INTO dbo.yourtable( [Client_Id], [Project_Id], [Hotel_Id], [Room_Type], [Room_Category], [Allotment_Date], 
        [Number_Of_Rooms], [Number_Booked], [Number_Available], [Overbook], [Price], [Dep_Amount], [Full_Payment], 
        [Admin_Only], [HotelAllotment_Id], [Price_Excl_VAT], [VAT_Code], [Charge_Dep_Amount] )
    SELECT  'DEFAULT', 'TEMPLATE', 2423, 276, NULL, DateVal, 99999, 1, 99998, 0, 33000, 0, 1, 0, 279611, 0, NULL, 0
    FROM    @t_Date;
END;
GO

SELECT  COUNT( DISTINCT Allotment_date )
FROM    dbo.yourtable;

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

select @cols = STUFF((SELECT ',' + QUOTENAME(convert(varchar(10), allotment_date, 120)) 
                    from yourtable
                    group by allotment_date
                    order by allotment_date
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = '
            SELECT  *
            FROM (    SELECT client_id, project_id, hotel_id,
                        room_type, room_category,' + @cols + ' 
                    from 
                    (
                      select client_id, project_id, hotel_id,
                        room_type, room_category,
                        allotment_date, number_booked
                      from yourtable
                    ) x
                    pivot 
                    (
                        sum(number_booked)
                        for allotment_date in (' + @cols + ')
                    ) p ) a;'

execute sp_executesql @query;
GO