Je me demande comment convertir des valeurs délimitées par des virgules en lignes dans Redshift. J'ai peur que ma propre solution ne soit pas optimale. S'il vous plaît donnez votre avis. J'ai une table avec l'une des colonnes avec des valeurs séparées par un coma. Par exemple:
J'ai:
user_id|user_name|user_action
-----------------------------
1 | Shone | start,stop,cancell...
J'aimerais voir
user_id|user_name|parsed_action
-------------------------------
1 | Shone | start
1 | Shone | stop
1 | Shone | cancell
....
Une légère amélioration par rapport à la réponse existante consiste à utiliser une deuxième table "nombres" qui énumère toutes les longueurs de liste possibles, puis à utiliser un cross join
pour rendre la requête plus compacte.
Redshift n'a pas de méthode simple pour créer une table de nombres que je connaisse, mais nous pouvons utiliser un peu de piratage de https://www.periscope.io/blog/generate-series-in- redshift-and-mysql.html pour en créer un en utilisant des numéros de ligne.
Plus précisément, si nous supposons le nombre de lignes dans cmd_logs
est supérieur au nombre maximal de virgules dans user_action
colonne, nous pouvons créer une table de nombres en comptant les lignes. Pour commencer, supposons qu'il y ait au plus 99 virgules dans le user_action
colonne:
select
(row_number() over (order by true))::int as n
into numbers
from cmd_logs
limit 100;
Si nous voulons être fantaisistes, nous pouvons calculer le nombre de virgules à partir de cmd_logs
table pour créer un ensemble de lignes plus précis dans 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;
Une fois qu'il y a une table numbers
, on peut faire:
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) != '';
Vous pouvez obtenir le résultat attendu avec la requête suivante. J'utilise "UNION ALL" pour convertir une colonne en ligne.
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
Une autre idée consiste à transformer d'abord votre chaîne CSV en JSON, puis à extraire JSON, comme suit:
... '["' || replace( user_action, '.', '", "' ) || '"]' AS replaced
... JSON_EXTRACT_ARRAY_ELEMENT_TEXT(replaced, numbers.i) AS parsed_action
Où "nombres" est le tableau de la première réponse. L'avantage de cette approche est la possibilité d'utiliser la fonctionnalité JSON intégrée.
Tard dans la soirée mais j'ai eu quelque chose qui fonctionnait (quoique très lent cependant)
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)
Merci à réponse de Bob Baxley pour l'inspiration
Juste amélioration pour la réponse ci-dessus https://stackoverflow.com/a/31998832/1265306
Génère une table de nombres en utilisant le SQL suivant 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" est là uniquement si vous voulez le coller sans la clause INTO et voir les résultats
Voici ma réponse tout aussi terrible.
J'ai une table users
, puis une table events
avec une colonne qui n'est qu'une chaîne d'utilisateurs délimitée par des virgules lors dudit événement. par exemple
event_id | user_ids
1 | 5,18,25,99,105
Dans ce cas, j'ai utilisé les fonctions LIKE
et les caractères génériques pour créer une nouvelle table qui représente chaque Edge utilisateur de l'événement.
SELECT e.event_id, u.id as user_id
FROM events e
LEFT JOIN users u ON e.user_ids like '%' || u.id || '%'
Ce n'est pas joli, mais je le jette dans une clause WITH
pour ne pas avoir à l'exécuter plus d'une fois par requête. Je vais probablement construire un ETL pour créer cette table tous les soirs de toute façon.
En outre, cela ne fonctionne que si vous avez une deuxième table qui ne a une ligne par possibilité unique. Si ce n'est pas le cas, vous pouvez faire LISTAGG
pour obtenir une seule cellule avec toutes vos valeurs, l'exporter vers un CSV et re-upload that comme un tableau pour vous aider.
Comme je l'ai dit: une terrible et mauvaise solution.