我想知道如何在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
....
答案 0 :(得分:25)
对现有答案的略微改进是使用第二个“数字”表来枚举所有可能的列表长度,然后使用cross join
使查询更紧凑。
Redshift没有一种简单的方法来创建我所知道的数字表,但是我们可以使用https://www.periscope.io/blog/generate-series-in-redshift-and-mysql.html中的一些hack来创建一个使用行号的数据库。
具体来说,如果我们假设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) != '';
答案 1 :(得分:2)
您可以使用以下查询获得预期结果。我正在使用&#34; UNION ALL&#34;将列转换为行。
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 :(得分:1)
另一个想法是首先将您的CSV字符串转换为JSON,然后按照以下行将JSON提取转换为:
... '["' || replace( user_action, '.', '", "' ) || '"]' AS replaced
... JSON_EXTRACT_ARRAY_ELEMENT_TEXT(replaced, numbers.i) AS parsed_action
其中&#34;数字&#34;是第一个答案的表格。这种方法的优点是能够使用内置的JSON功能。
答案 3 :(得分:0)
这是我同样可怕的答案。
我有一个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并重新上载 作为表格以提供帮助。
答案 4 :(得分:0)
参加派对的时间已晚,但我有一些工作(虽然虽然很慢)
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)
感谢answer by Bob Baxley获取灵感
答案 5 :(得分:0)
仅针对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子句并且看到结果的情况下
答案 6 :(得分:0)
创建一个存储过程,该过程将动态分析字符串并填充temp表,从temp表中选择。
这是魔术代码:-
CREATE OR REPLACE PROCEDURE public.sp_string_split( "string" character varying )
AS $$
DECLARE
cnt INTEGER := 1;
no_of_parts INTEGER := (select REGEXP_COUNT ( string , ',' ));
sql VARCHAR(MAX) := '';
item character varying := '';
BEGIN
-- Create table
sql := 'CREATE TEMPORARY TABLE IF NOT EXISTS split_table (part VARCHAR(255)) ';
RAISE NOTICE 'executing sql %', sql ;
EXECUTE sql;
<<simple_loop_exit_continue>>
LOOP
item = (select split_part("string",',',cnt));
RAISE NOTICE 'item %', item ;
sql := 'INSERT INTO split_table SELECT '''||item||''' ';
EXECUTE sql;
cnt = cnt + 1;
EXIT simple_loop_exit_continue WHEN (cnt >= no_of_parts + 2);
END LOOP;
END ;
$$ LANGUAGE plpgsql;
用法示例:-
call public.sp_string_split('john,smith,jones');
select *
from split_table
答案 7 :(得分:0)
如果您知道 user_action 列中没有很多操作,您可以使用带有 union all
的递归子查询,从而避免使用 aux numbers
表。
但是它需要您知道每个用户的操作次数,要么调整初始表,要么为其创建视图或临时表。
数据准备
假设你有这样一个表格:
create temporary table actions
(
user_id varchar,
user_name varchar,
user_action varchar
);
我将在其中插入一些值:
insert into actions
values (1, 'Shone', 'start,stop,cancel'),
(2, 'Gregory', 'find,diagnose,taunt'),
(3, 'Robot', 'kill,destroy');
这是一个带有临时计数的附加表
create temporary table actions_with_counts
(
id varchar,
name varchar,
num_actions integer,
actions varchar
);
insert into actions_with_counts (
select user_id,
user_name,
regexp_count(user_action, ',') + 1 as num_actions,
user_action
from actions
);
这将是我们的“输入表”,它看起来和您预期的一样
select * from actions_with_counts;
id | 名称 | num_actions | 动作 |
---|---|---|---|
2 | 格雷戈里 | 3 | 查找、诊断、嘲讽 |
3 | 机器人 | 2 | 杀死,摧毁 |
1 | 闪耀 | 3 | 开始、停止、取消 |
同样,您可以调整初始表,从而跳过将计数添加为单独的表。
用于扁平化操作的子查询
这是取消嵌套的查询:
with recursive tmp (user_id, user_name, idx, user_action) as
(
select id,
name,
1 as idx,
split_part(actions, ',', 1) as user_action
from actions_with_counts
union all
select user_id,
user_name,
idx + 1 as idx,
split_part(actions, ',', idx + 1)
from actions_with_counts
join tmp on actions_with_counts.id = tmp.user_id
where idx < num_actions
)
select user_id, user_name, user_action as parsed_action
from tmp
order by user_id;
这将为每个操作创建一个新行,输出如下所示:
user_id | user_name | parsed_action |
---|---|---|
1 | 闪耀 | 开始 |
1 | 闪耀 | 停止 |
1 | 闪耀 | 取消 |
2 | 格雷戈里 | 查找 |
2 | 格雷戈里 | 诊断 |
2 | 格雷戈里 | 嘲讽 |
3 | 机器人 | kill |
3 | 机器人 | 销毁 |
答案 8 :(得分:-3)
您可以尝试复制命令将文件复制到红移表
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 ','
您可以使用分隔符','选项。
有关复制命令选项的更多详细信息,请访问此页面