2014-02-05 3 views
0

Мне нужна помощь с таблицами PIVOT или чем-то, чтобы получить результат по-моему.Строки с датами в столбцах - SQL Server 2008

У меня есть такой стол.

+---------------+--------------+----------+-----------+---------------+-----------------------------------------+-----------------+---------------+------------------+----------+-------+------------+--------------+------------+-------------------+----------------+----------+-------------------+ 
|  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 (если таковые имеются)

мне это нужно, чтобы быть динамичным, потому что даты могут измениться.

Я пробовал использовать простые сводные таблицы без везения.

Любая помощь будет отличной.

ответ

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 | 
+0

Это хорошо, но с большим проектом, в котором есть много отелей, а диапазон дат - не то же самое для каждого отеля, они дают ошибку. «Msg 1056, уровень 15, состояние 1, строка 1 Количество элементов в списке выбора превышает максимально допустимое число 4096 элементов». Проект, который я тестировал, имеет 4641 запись –

+2

@FedericoGiust Существуют ограничения на месте что может быть возвращено, если вам действительно нужно развернуть 4641 различные значения данных, тогда вам следует рассмотреть возможность разбивки на более мелкие части или сделать этот тип преобразования на вашем прикладном уровне, а не в SQL. – Taryn

+3

@FedericoGiust Затем это знак того, что то, что вы делаете, не то, что должно быть сделано в слое базы данных – Lamak

2

EDIT: Дальнейшее обсуждение этого «решения» указало, что было бы целесообразно, чтобы вдаваться в детали, чтобы почему это никогда не должны использоваться в качестве надежного кода , Вне очевидных NEVER EVER, таких как SELECT *, переход по этому пути только превосходит ограничение времени компиляции. Предположим, что мы населили переменную @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

Internal error: An expression services limit has been reached. Please look for potentially complex expressions in your query, and try to simplify them.

Примечание что даже сообщение об ошибке можно суммировать, поскольку @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 
Смежные вопросы