Vertica динамический поворот / преобразование - PullRequest
0 голосов
/ 09 февраля 2019

У меня есть таблица в vertica:

id   Timestamp    Mask1       Mask2
------------------------------------------- 
 1    11:30         50         100
 1    11:35         52         101 
 2    12:00         53         102
 3    09:00         50         100
 3    22:10         52         105
 .     .            .           .
 .     .            .           .

, которую я хочу преобразовать в:

 id    rows     09:00    11:30    11:35     12:00     22:10     ....... 
-------------------------------------------------------------- 
 1     Mask1     Null     50       52       Null       Null     ....... 
       Mask2     Null     100      101      Null       Null     ....... 
 2     Mask1     Null     Null     Null     53         Null     .......     
       Mask2     Null     Null     Null     102        Null     .......
 3     Mask1     50       Null     Null     Null       52       .......     
       Mask2     100      Null     Null     Null       105      .......

Точки (...) указывают, что у меня много записей.

  1. Отметка времени указана за целый день и имеет формат часов: минут: секунд, начиная с 00:00:00 до 24:00:00 за день (я только что использовал часы: минуты для вопроса).
  2. Я определил только два дополнительных столбца Mask1 и Mask2.У меня есть около 200 столбцов маски для работы.
  3. Я показал 5 записей, но на самом деле у меня есть около миллиона записей.

Что я пробовал до сих пор:

  1. Сброс каждой записи на основе идентификатора в файле CSV.
  2. Применение транспонирования в пандах Python.
  3. Объединение транспонированных таблиц.

TheВозможным общим решением может быть поворот в vertica (или UDTF), но я довольно новичок в этой базе данных.

Я борюсь с этой логикой в ​​течение пары дней.Кто-нибудь может мне помочь, пожалуйста.Большое спасибо.

Ответы [ 2 ]

0 голосов
/ 11 февраля 2019

Ниже приведено решение, так как я бы кодировал его только для значений времени, которые есть в ваших примерах данных.

Если вы действительно хотите отображать все 86400 от '00:00:00' до '23:59:59'Впрочем, ты не сможешь.Максимальное количество столбцов в Vertica составляет 1600.

Однако вы можете поиграть с функцией Vertica TIME_SLICE(timestamp::TIMESTAMP,1,'MINUTE')::TIME

(TIME_SLICE принимает метку времени в качестве входного и возвращает метку времени, поэтому вам нужно привести(::) и обратно), чтобы уменьшить количество строк до 1440 ...

В любом случае, я бы начал с SELECT DISTINCT timestamp FROM input ORDER BY 1;, а затем в последнем запросе сгенерировал бы одинстрока на найденную метку времени (надеясь, что они не будут превышать 1598 ....), как те, которые фактически используются для ваших данных, в ваш запрос:

, SUM(CASE timestamp WHEN '09:00' THEN val END) AS "09:00"
, SUM(CASE timestamp WHEN '11:30' THEN val END) AS "11:30"
, SUM(CASE timestamp WHEN '11:35' THEN val END) AS "11:35"
, SUM(CASE timestamp WHEN '12:00' THEN val END) AS "12:00"
, SUM(CASE timestamp WHEN '22:10' THEN val END) AS "22:10"

В общем случае SQL не имеет переменного числавыходные столбцы из любого запроса.Если число последних столбцов зависит от данных, вам нужно будет сгенерировать окончательный запрос на основе данных и затем запустить его.

Добро пожаловать в SQL и реляционные базы данных ..

Вот полный скрипт для ваших данных.Сначала я поворачиваюсь вертикально, вдоль имен столбцов «Маска-н», а затем поворачиваюсь горизонтально, вдоль отметок времени.

\pset null Null
-- ^ this is a vsql command to display nulls with the "Null" string
WITH 
-- your input, not in final query
input(id,Timestamp,Mask1,Mask2) AS (
          SELECT 1 ,  TIME '11:30'    ,    50    ,    100
UNION ALL SELECT 1 ,  TIME '11:35'    ,    52    ,    101
UNION ALL SELECT 2 ,  TIME '12:00'    ,    53    ,    102
UNION ALL SELECT 3 ,  TIME '09:00'    ,    50    ,    100
UNION ALL SELECT 3 ,  TIME '22:10'    ,    52    ,    105
)
,
-- real WITH clause starts here
-- need an index for your 200 masks
i(i) AS (
  SELECT MICROSECOND(ts) FROM (
            SELECT TIMESTAMPADD(MICROSECOND,  1,TIMESTAMP '2000-01-01') AS tm
  UNION ALL SELECT TIMESTAMPADD(MICROSECOND,200,TIMESTAMP '2000-01-01') AS tm
  )x
  TIMESERIES ts AS '1 MICROSECOND' OVER(ORDER BY tm)
)
,
-- verticalised masks
vertical AS (
  SELECT
    id
  , i
  , CASE i 
      WHEN   1 THEN 'Mask001' 
      WHEN   2 THEN 'Mask002' 
      WHEN 200 THEN 'Mask200' 
    END AS rows
  , timestamp
  , CASE i
      WHEN   1 THEN Mask1 
      WHEN   2 THEN Mask2 
      WHEN 200 THEN 0 -- no mask200 present
    END AS val
  FROM input CROSS JOIN i
  WHERE i <=2 -- only 2 masks present currently
)
-- test the vertical CTE ...
-- SELECT * FROM vertical order by id,rows,timestamp;
-- out  id | i |  rows   | timestamp | val 
-- out ----+---+---------+-----------+-----
-- out   1 | 1 | Mask001 | 11:30:00  |  50
-- out   1 | 1 | Mask001 | 11:35:00  |  52
-- out   1 | 2 | Mask002 | 11:30:00  | 100
-- out   1 | 2 | Mask002 | 11:35:00  | 101
-- out   2 | 1 | Mask001 | 12:00:00  |  53
-- out   2 | 2 | Mask002 | 12:00:00  | 102
-- out   3 | 1 | Mask001 | 09:00:00  |  50
-- out   3 | 1 | Mask001 | 22:10:00  |  52
-- out   3 | 2 | Mask002 | 09:00:00  | 100
-- out   3 | 2 | Mask002 | 22:10:00  | 105
SELECT
  id
, rows
, SUM(CASE timestamp WHEN '09:00' THEN val END) AS "09:00"
, SUM(CASE timestamp WHEN '11:30' THEN val END) AS "11:30"
, SUM(CASE timestamp WHEN '11:35' THEN val END) AS "11:35"
, SUM(CASE timestamp WHEN '12:00' THEN val END) AS "12:00"
, SUM(CASE timestamp WHEN '22:10' THEN val END) AS "22:10"
FROM vertical
GROUP BY
  id
, rows
ORDER BY
  id
, rows
;
-- out Null display is "Null".
-- out  id |  rows   | 09:00 | 11:30 | 11:35 | 12:00 | 22:10 
-- out ----+---------+-------+-------+-------+-------+-------
-- out   1 | Mask001 |  Null |    50 |    52 |  Null |  Null
-- out   1 | Mask002 |  Null |   100 |   101 |  Null |  Null
-- out   2 | Mask001 |  Null |  Null |  Null |    53 |  Null
-- out   2 | Mask002 |  Null |  Null |  Null |   102 |  Null
-- out   3 | Mask001 |    50 |  Null |  Null |  Null |    52
-- out   3 | Mask002 |   100 |  Null |  Null |  Null |   105
-- out (6 rows)
-- out 
-- out Time: First fetch (6 rows): 28.143 ms. All rows formatted: 28.205 ms
0 голосов
/ 09 февраля 2019

Вы можете использовать union all, чтобы отменить вывод данных, а затем условное агрегирование:

select id, which,
       max(case when timestamp >= '09:00' and timestamp < '09:30'  then mask end) as "09:00",
       max(case when timestamp >= '09:30' and timestamp < '10:00' then mask end) as "09:30",
       max(case when timestamp >= '10:00' and timestamp < '10:30' then mask end) as "10:00",
       . . .
from ((select id, timestamp,
              'Mask1' as which, Mask1 as mask
       from t
      ) union all
      (select id, timestamp, 'Mask2' as which, Mask2 as mask
       from t
      ) 
     ) t
group by t.id, t.which;

Примечание. Сюда входит id в каждой строке.Я настоятельно рекомендую сделать это, но вы можете использовать:

select (case when which = 'Mask1' then id end) as id

Если вы действительно хотите.

...