在 RedShift 中将值拆分为多行
Split values over multiple rows in RedShift
如何将字段(例如CSV字符串)拆分为多行的问题已经得到解答:
Split values over multiple rows.
但是,这个问题指的是 MSSQL,答案使用了各种没有 RedShift 等效项的功能。
为了完整起见,这里有一个我想做的例子:
当前数据:
| Key | Data |
+-----+----------+
| 1 | 18,20,22 |
| 2 | 17,19 |
所需数据:
| Key | Data |
+-----+----------+
| 1 | 18 |
| 1 | 20 |
| 1 | 22 |
| 2 | 17 |
| 2 | 19 |
现在,对于 CSV 字段中元素数量有限的情况,我可以建议一种解决方法:使用 split_part 并合并所有可能的数组位置,如下所示:
SELECT Key, split_part(Data, ',', 1)
FROM mytable
WHERE split_part(Data, ',', 1) != ""
UNION
SELECT Key, split_part(Data, ',', 2)
FROM mytable
WHERE split_part(Data, ',', 2) != ""
-- etc. etc.
但是,这显然效率很低,并且不适用于较长的列表。
关于如何做到这一点有更好的想法吗?
编辑:
还有一个关于乘法行的类似问题:splitting rows in Redshift。但是,我看不到如何在此处应用这种方法。
编辑 2:
可能重复:Redshift. Convert comma delimited values into rows。但没有什么新鲜事 - @Masashi Miyazaki 的回答与我上面的建议相似,并且遇到同样的问题。
欢迎使用 RDS PostgreSql 实例并创建到 RedShift 的 dblink。然后您可以像在普通 PostgreSQL 数据库上一样操作结果集,甚至可以通过相同的 dblink 将结果放回 RedShift。
这是 Redshift 的答案,每行最多可处理 10,000 个值。
设置测试数据
create table test_data (key varchar(50),data varchar(max));
insert into test_data
values
(1,'18,20,22'),
(2,'17,19')
;
代码
with ten_numbers as (select 1 as num union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0)
, generted_numbers AS
(
SELECT (1000 * t1.num) + (100 * t2.num) + (10 * t3.num) + t4.num AS gen_num
FROM ten_numbers AS t1
JOIN ten_numbers AS t2 ON 1 = 1
JOIN ten_numbers AS t3 ON 1 = 1
JOIN ten_numbers AS t4 ON 1 = 1
)
, splitter AS
(
SELECT *
FROM generted_numbers
WHERE gen_num BETWEEN 1 AND (SELECT max(REGEXP_COUNT(data, '\,') + 1)
FROM test_data)
)
, expanded_input AS
(
SELECT
key,
split_part(data, ',', s.gen_num) AS data
FROM test_data AS td
JOIN splitter AS s ON 1 = 1
WHERE split_part(data, ',', s.gen_num) <> ''
)
SELECT * FROM expanded_input
order by key,data;
如何将字段(例如CSV字符串)拆分为多行的问题已经得到解答: Split values over multiple rows.
但是,这个问题指的是 MSSQL,答案使用了各种没有 RedShift 等效项的功能。
为了完整起见,这里有一个我想做的例子:
当前数据:
| Key | Data |
+-----+----------+
| 1 | 18,20,22 |
| 2 | 17,19 |
所需数据:
| Key | Data |
+-----+----------+
| 1 | 18 |
| 1 | 20 |
| 1 | 22 |
| 2 | 17 |
| 2 | 19 |
现在,对于 CSV 字段中元素数量有限的情况,我可以建议一种解决方法:使用 split_part 并合并所有可能的数组位置,如下所示:
SELECT Key, split_part(Data, ',', 1)
FROM mytable
WHERE split_part(Data, ',', 1) != ""
UNION
SELECT Key, split_part(Data, ',', 2)
FROM mytable
WHERE split_part(Data, ',', 2) != ""
-- etc. etc.
但是,这显然效率很低,并且不适用于较长的列表。
关于如何做到这一点有更好的想法吗?
编辑:
还有一个关于乘法行的类似问题:splitting rows in Redshift。但是,我看不到如何在此处应用这种方法。
编辑 2:
可能重复:Redshift. Convert comma delimited values into rows。但没有什么新鲜事 - @Masashi Miyazaki 的回答与我上面的建议相似,并且遇到同样的问题。
欢迎使用 RDS PostgreSql 实例并创建到 RedShift 的 dblink。然后您可以像在普通 PostgreSQL 数据库上一样操作结果集,甚至可以通过相同的 dblink 将结果放回 RedShift。
这是 Redshift 的答案,每行最多可处理 10,000 个值。
设置测试数据
create table test_data (key varchar(50),data varchar(max));
insert into test_data
values
(1,'18,20,22'),
(2,'17,19')
;
代码
with ten_numbers as (select 1 as num union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0)
, generted_numbers AS
(
SELECT (1000 * t1.num) + (100 * t2.num) + (10 * t3.num) + t4.num AS gen_num
FROM ten_numbers AS t1
JOIN ten_numbers AS t2 ON 1 = 1
JOIN ten_numbers AS t3 ON 1 = 1
JOIN ten_numbers AS t4 ON 1 = 1
)
, splitter AS
(
SELECT *
FROM generted_numbers
WHERE gen_num BETWEEN 1 AND (SELECT max(REGEXP_COUNT(data, '\,') + 1)
FROM test_data)
)
, expanded_input AS
(
SELECT
key,
split_part(data, ',', s.gen_num) AS data
FROM test_data AS td
JOIN splitter AS s ON 1 = 1
WHERE split_part(data, ',', s.gen_num) <> ''
)
SELECT * FROM expanded_input
order by key,data;