Redshift. Преобразование значений, разделенных запятыми в строки

Мне интересно, как преобразовать значения, разделенные запятой, в строки в Redshift. Я боюсь, что мое собственное решение не оптимально. Пожалуйста посоветуй. У меня есть таблица с одним из столбцов с комами, разделенными значениями. Например:

У меня есть:

user_id|user_name|user_action
-----------------------------
1      | Shone   | start,stop,cancell...

Я хотел бы видеть

user_id|user_name|parsed_action 
------------------------------- 
1      | Shone   | start        
1      | Shone   | stop         
1      | Shone   | cancell      
....

Ответы

Ответ 1

Небольшое улучшение по сравнению с существующим ответом заключается в использовании второй таблицы "числа", которая перечисляет все возможные длины списков, а затем использует cross join, чтобы сделать запрос более компактным.

Redshift не имеет прямого метода для создания таблицы чисел, о которой я знаю, но мы можем использовать немного взлома из https://www.periscope.io/blog/generate-series-in-redshift-and-mysql.html для создания один из которых использует номера строк.

В частности, если предположить, что число строк в cmd_logs больше максимального числа запятых в столбце user_action, мы можем создать таблицу чисел, подсчитывая строки. Для начала предположим, что в столбце user_action не более 99 запятых:

select 
  row_number() over (order by true) as n
into numbers
from cmd_logs
limit 100;

Если мы хотим получить фантазию, мы можем вычислить количество запятых из таблицы cmd_logs, чтобы создать более точный набор строк в numbers:

select
  n::int
into numbers
from
  (select 
      row_number() over (order by true) as n
   from cmd_logs)
cross join
  (select 
      max(regexp_count(user_action, '[,]')) as max_num 
   from cmd_logs)
where
  n <= max_num + 1;

Когда есть таблица numbers, мы можем сделать:

select
  user_id, 
  user_name, 
  split_part(user_action,',',n) as parsed_action 
from
  cmd_logs
cross join
  numbers
where
  split_part(user_action,',',n) is not null
  and split_part(user_action,',',n) != '';

Ответ 2

Еще одна идея - сначала преобразовать вашу строку CSV в JSON, а затем извлечь JSON в следующих строках:

... '["' || replace( user_action, '.', '", "' ) || '"]' AS replaced

... JSON_EXTRACT_ARRAY_ELEMENT_TEXT(replaced, numbers.i) AS parsed_action

Где "числа" - таблица из первого ответа. Преимуществом этого подхода является возможность использования встроенных функций JSON.

Ответ 3

Вы можете получить ожидаемый результат со следующим запросом. Я использую "UNION ALL" для преобразования столбца в строку.

select user_id, user_name, split_part(user_action,',',1) as parsed_action from cmd_logs
union all
select user_id, user_name, split_part(user_action,',',2) as parsed_action from cmd_logs
union all
select user_id, user_name, split_part(user_action,',',3) as parsed_action from cmd_logs

Ответ 4

Вот мой не менее страшный ответ.

У меня есть таблица users, а затем таблица events с столбцом, который представляет собой только строку пользователей с разделителями-запятыми в указанном событии. например,

event_id | user_ids
1        | 5,18,25,99,105

В этом случае я использовал функции LIKE и подстановочные знаки для создания новой таблицы, которая представляет каждый край пользовательского интерфейса.

SELECT e.event_id, u.id as user_id
FROM events e
LEFT JOIN users u ON e.user_ids like '%' || u.id || '%'

Это не очень, но я бросаю его в предложение WITH, так что мне не нужно запускать его более одного раза за запрос. Я, скорее всего, просто создам ETL, чтобы каждую неделю создавать эту таблицу.

Кроме того, это работает только если у вас есть вторая таблица, у которой есть одна строка за уникальную возможность. Если нет, вы можете сделать LISTAGG, чтобы получить одну ячейку со всеми вашими значениями, экспортировать ее в CSV и повторно загрузить в качестве таблицы.

Как я сказал: ужасное, нехорошее решение.

Ответ 5

Поздно к партии, но у меня что-то работает (хотя и очень медленно)

with nums as (select n::int n
from
  (select 
      row_number() over (order by true) as n
   from table_with_enough_rows_to_cover_range)
cross join
  (select 
      max(json_array_length(json_column)) as max_num 
   from table_with_json_column )
where
  n <= max_num + 1)
select *, json_extract_array_element_text(json_column,nums.n-1) parsed_json
from  nums, table_with_json_column
where json_extract_array_element_text(json_column,nums.n-1) != ''
and nums.n <= json_array_length(json_column) 

Благодаря ответ Боба Бэкли для вдохновения

Ответ 6

Вы можете попробовать скопировать команду, чтобы скопировать файл в таблицы красного смещения

copy table_name from 's3://mybucket/myfolder/my.csv' CREDENTIALS 'aws_access_key_id=my_aws_acc_key;aws_secret_access_key=my_aws_sec_key' delimiter ','

Вы можете использовать опцию разделитель,.

Для получения дополнительной информации о параметрах команды копирования вы можете посетить эту страницу

http://docs.aws.amazon.com/redshift/latest/dg/r_COPY.html