【问题标题】:Rows with dates into columns - SQL Server 2008日期成列的行 - SQL Server 2008
【发布时间】:2014-03-02 00:32:38
【问题描述】:

我需要一些关于 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(如果有)分组

我需要它是动态的,因为日期可能会改变。

我尝试使用简单的数据透视表,但没有成功。

任何帮助都会很棒。

【问题讨论】:

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


    【解决方案1】:

    编辑:关于这个“解决方案”的进一步讨论表明,谨慎的做法是进一步详细说明为什么这不应该被用作可靠的代码。除了像SELECT * 这样明显的 NEVER EVER 之外,沿着这条路走只会超过编译时间限制。假设我们用以下 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;
    

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

    消息 8632,第 17 级,状态 2,第 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
    

    【讨论】:

      【解决方案2】:

      在编写动态版本之前,您可以使用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 |
      

      【讨论】:

      • 这很好,但是对于有很多酒店的大型项目并且每个酒店的日期范围不同,它会给出错误。 “Msg 1056, Level 15, State 1, Line 1 选择列表中的元素数量超过了最大允许的 4096 个元素数量。” 我正在测试的项目有 4641 条记录
      • @FedericoGiust 对于可以返回的内容存在限制,如果您确实需要对 4641 个不同的数据值进行透视,那么您应该考虑将其分解为更小的部分或进行此类转换在您的应用程序层中而不是在 SQL 中。
      • @FedericoGiust 那么这表明你正在做的事情不应该在数据库层完成
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多