SQL Pivot с текущим статусом - PullRequest
0 голосов
/ 17 апреля 2020

Я использую SQL server 2014, и у меня есть следующий набор данных ниже. Он содержит текущий и исторический статус сериализованного розничного товара:

SERIAL      STATUS      UPDATE_DATE
123456789   Received    2020-03-04 10:33:10.0000000
123456789   Scanned     2020-03-04 17:00:32.0000000
123456789   Triaged     2020-03-04 17:04:30.0000000
123456789   Sold        2020-04-03 20:34:57.0000000

Я построил следующий запрос для горизонтального поворота дат состояния.

SELECT * FROM(
SELECT SERIAL, [STATUS], [UPDATE_DATE]
FROM #TEMP ) AS PivotTable
PIVOT(MIN (UPDATE_DATE) FOR [STATUS] IN ([Received],[Scanned],[Triaged],[Sold])) AS P1

Это прекрасно работает, и сгенерировал следующий вывод:

SERIAL      Received            Scanned             Triaged             Sold
123456789   2020-03-04 10:33    2020-03-04 17:00    2020-03-04 17:04    2020-04-03 20:34

Однако я также собираюсь включить статус самого последнего элемента. Как мне добиться этого?

Желаемый вывод:

SERIAL      STATUS    Received          Scanned             Triaged             Sold
123456789   Sold      2020-03-04 10:33  2020-03-04 17:00    2020-03-04 17:04    2020-04-03 20:34

Вот код для построения временной таблицы:

SELECT * INTO #TEMP
FROM (
SELECT N'123456789' AS SERIAL, N'Received' AS [STATUS], N'2020-03-04 10:33' AS [UPDATE_DATE] UNION 
ALL
SELECT N'123456789' AS SERIAL, N'Scanned' AS [STATUS], N'2020-03-04 17:00' AS [UPDATE_DATE] UNION ALL
SELECT N'123456789' AS SERIAL, N'Triaged' AS [STATUS], N'2020-03-04 17:04' AS [UPDATE_DATE] UNION ALL
SELECT N'123456789' AS SERIAL, N'Sold' AS [STATUS], N'2020-04-03 20:34' AS [UPDATE_DATE] ) t;

Ответы [ 3 ]

1 голос
/ 17 апреля 2020

В последних sql версиях сервера вы можете использовать last_value

SELECT * 
FROM(
  SELECT SERIAL, [STATUS], [UPDATE_DATE]
    , last_value([STATUS]) over(partition by SERIAL order by UPDATE_DATE rows between unbounded preceding and unbounded following) lastStatus
  FROM #TEMP ) AS PivotTable
  PIVOT(MIN (UPDATE_DATE) FOR [STATUS] IN ([Received],[Scanned],[Triaged],[Sold])) AS P1
1 голос
/ 17 апреля 2020

Если у вас есть переменный номер статуса, вы можете создать динамический c пивот, как показано ниже.

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

SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.STATUS) 
            FROM temp c
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = '

if object_id(''TempNew'') is not null
begin
    drop table TempNew
end

SELECT * INTO TempNew FROM (SELECT SERIAL, ' + @cols + ' from 
            (
                select SERIAL
                    , UPDATE_DATE
                    , STATUS
                from temp
           ) x
            pivot 
            (
                max(UPDATE_DATE)
                for STATUS in (' + @cols + ')
            )a) p '

--select @query
execute(@query)

select 
   * 
from TempNew 
cross join (
  Select top 1 STATUS as [Status] From Temp order by UPDATE_DATE desc
)a

Запрос был разделен на две части. В первой части создается таблица для статуса даты, который был объединен с максимальным статусом.

Live db <> fiddle demo.

1 голос
/ 17 апреля 2020

Использовать условную агрегацию:

select serial,
       max(case when seqnum = 1 then status end) as most_recent_status,
       max(case when status = 'Received' then update_date end) as received,
       max(case when status = 'Scanned' then update_date end) as scanned,
       max(case when status = 'Triaged' then update_date end) as triaged,
       max(case when status = 'Sold' then update_date end) as sold
from (select t.*,
             row_number() over (partition by serial order by update_date desc) as seqnum
      from #temp t
     ) t
group by serial;
...