如何将以分号分隔的列名和值字符串转换为新的 table

How to transform semicolon separated strings of column names and values into a new table

我对 Snowflake 比较陌生,在为半结构化数据集设置转换方面遇到了一些困难。我有几个日志数据批次,其中每个批次(Snowflake 中的 table 行)都有以下列:LOG_ID、COLUMN_NAMES 和 LOG_ENTRIES。

COLUMN_NAMES 包含以分号分隔的列名列表,例如:

“TIMESTAMP;Sensor A;Sensor B”, “TIMESTAMP;Sensor B;Sensor C”

LOG_ENTRIES:条目包含以分号分隔的值列表,例如

“2020-02-11 09:08:19; 99.24;12.25”

日志批次(雪花行)之间的 COLUMN_NAMES 字符串可能不同,但按它们出现顺序排列的名称描述同一行的 LOG_ENTRIES 列值的内容。我的目标是将数据转换为 table,其中包含 COLUMN_NAMES 列中存在的所有唯一值的列名,例如:

LOG_ID TIMESTAMP Sensor A Sensor B Sensor C
1 2020-02-11 09:08:19 99.24 12.25 NaN
2 2020-02-11 09:10:44 NaN 13.32 0.947

这可以用雪花脚本实现吗?如果可以,怎么做? :)

此致, 约翰

你应该使用SPLIT_TO_TABLE函数,拆分两个值,并通过索引连接它们。 之后,您所要做的就是使用 PIVOT 反转 table.

示例数据:

create or replace table splittable (LOG_ID int, COLUMN_NAMES varchar, LOG_ENTRIES varchar);
insert into splittable (LOG_ID, COLUMN_NAMES, LOG_ENTRIES) 
values (1, 'TIMESTAMP;Sensor A;Sensor B', '2020-02-11 09:08:19;99.24;12.25'), 
       (2, 'TIMESTAMP;Sensor B;Sensor C', '2020-02-11 09:10:44;13.32;0.947');

解决方案建议:

WITH src AS (
  select LOG_ID, cn.VALUE as COLUMN_NAMES, le.VALUE as LOG_ENTRIES 
      from splittable as st, 
      lateral split_to_table(st.COLUMN_NAMES, ';') as cn,
      lateral split_to_table(st.LOG_ENTRIES, ';') as le
    where cn.INDEX = le.INDEX
)  
select * from src 
pivot (min(LOG_ENTRIES) for COLUMN_NAMES in ('TIMESTAMP','Sensor A','Sensor B','Sensor C'))
order by LOG_ID; 

参考:SPLIT_TO_TABLE, PIVOT

如果列列表是可变的并且您无法定义它,那么您必须编写一些生成器,也许它会有所帮助:CREATE A DYNAMIC PIVOT IN SNOWFLAKE

您可以将数据转换为 ACTUAL 半结构化数据类型,然后您可以使用 Snowflake 本机查询 SQL。

WITH x AS (
SELECT column_names, log_entries
FROM (VALUES ('TIMESTAMP_;SENSOR1','2021-02-01'||';1.2')) x (column_names, log_entries)
),
y AS (
    SELECT *
    FROM x,
    LATERAL FLATTEN(input => split(column_names,';')) f
),
z AS (
    SELECT *
    FROM x,
    LATERAL FLATTEN(input => split(log_entries,';')) f
)
SELECT listagg(('"'||y.value||'":"'||z.value||'"'),',') as cnt
     , parse_json('{'||cnt||'}') as var
FROM y
JOIN z
  ON y.seq = z.seq
 AND y.index = z.index
GROUP BY y.seq;