web-dev-qa-db-ja.com

レッドシフト。カンマ区切りの値を行に変換する

Redshiftでカンマ区切りの値を行に変換する方法を知りたいです。私自身の解決策は最適ではないようです。お知らせ下さい。カンマ区切りの値を持つ列の1つを持つテーブルがあります。例えば:

私が持っています:

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      
....
29
Yuri Levinsky

既存の回答に対するわずかな改善は、可能なすべてのリストの長さを列挙する2番目の「数値」テーブルを使用し、次に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))::int 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) != '';
26
Bob Baxley

次のクエリで期待される結果を得ることができます。 「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
2

別のアイデアは、CSV文字列を最初にJSONに変換し、次にJSON抽出を次の行に沿って変換することです。

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

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

「数字」は最初の回答からの表です。このアプローチの利点は、組み込みのJSON機能を使用できることです。

1
YakovK

パーティーに遅れましたが、何かうまくいきました(ただし非常に遅いですが)

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) 

インスピレーションを得るために Bob Baxleyによる回答 に感謝

0
aarbor

上記の答えを改善するだけです https://stackoverflow.com/a/31998832/1265306

次のSQLを使用して数値テーブルを生成しています https://discourse.looker.com/t/generating-a-numbers-table-in-mysql-and-redshift/482

SELECT 
  p0.n 
  + p1.n*2 
  + p2.n * POWER(2,2) 
  + p3.n * POWER(2,3)
  + p4.n * POWER(2,4)
  + p5.n * POWER(2,5)
  + p6.n * POWER(2,6)
  + p7.n * POWER(2,7) 
  as number  
INTO numbers
FROM  
  (SELECT 0 as n UNION SELECT 1) p0,  
  (SELECT 0 as n UNION SELECT 1) p1,  
  (SELECT 0 as n UNION SELECT 1) p2, 
  (SELECT 0 as n UNION SELECT 1) p3,
  (SELECT 0 as n UNION SELECT 1) p4,
  (SELECT 0 as n UNION SELECT 1) p5,
  (SELECT 0 as n UNION SELECT 1) p6,
  (SELECT 0 as n UNION SELECT 1) p7
ORDER BY 1
LIMIT 100

「ORDER BY」は、INTO句なしで貼り付けて結果を確認する場合にのみ存在します

0
naviram

これが私の恐ろしい答えです。

私は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句に入れて、クエリごとに2回以上実行する必要がないようにしています。とにかく、毎晩ETLを作成してそのテーブルを毎晩作成することになるでしょう。

また、これはdoesである2番目のテーブルが一意の可能性ごとに1行ある場合にのみ機能します。そうでない場合は、LISTAGGを実行してすべての値を含む単一のセルを取得し、それをCSVにエクスポートして、役立つテーブルとしてthatを再アップロードします。

私が言ったように:ひどい、良くない解決策。

0
ScottieB