在单独的行上返回Redshift JSON数组的元素

Ser*_*hia 4 json amazon-redshift

我有一个Redshift表,看起来像这样:

 id | metadata
---------------------------------------------------------------------------
 1  | [{"pet":"dog"},{"country":"uk"}]
 2  | [{"pet":"cat"}]
 3  | []
 4  | [{"country":"germany"},{"education":"masters"},{"country":"belgium"}]
Run Code Online (Sandbox Code Playgroud)
  • 所有数组元素只有一个字段。
  • 不能保证特定字段将出现在数组的任何元素中。
  • 字段名称可以在数组中重复
  • 数组元素可以任意顺序

我想找一张看起来像这样的桌子:

 id |   field   |  value
------------------------
 1  | pet       | dog
 1  | country   | uk
 2  | pet       | cat
 4  | country   | germany
 4  | education | masters
 4  | country   | belgium
Run Code Online (Sandbox Code Playgroud)

然后,我可以将其与对输入表其余部分的查询结合起来。

我曾尝试过使用Redshift JSON函数,但由于无法在Redshift中编写函数/使用循环/具有变量,我真的看不到这样做的方法!

请让我知道是否可以澄清其他问题。

Ser*_*hia 6

多亏了这篇启发性的博客文章,我得以制定解决方案。这是:

  1. 创建一个查找表,以有效地“迭代”每个数组的元素。该表中的行数等于或大于数组的最大元素数。假设这是4(可以使用来计算SELECT MAX(JSON_ARRAY_LENGTH(metadata)) FROM input_table):

    CREATE VIEW seq_0_to_3 AS
        SELECT 0 AS i UNION ALL                                      
        SELECT 1 UNION ALL
        SELECT 2 UNION ALL    
        SELECT 3          
    );
    
    Run Code Online (Sandbox Code Playgroud)
  2. 由此,我们可以为每个JSON元素创建一行:

    WITH exploded_array AS (                                                                          
        SELECT id, JSON_EXTRACT_ARRAY_ELEMENT_TEXT(metadata, seq.i) AS json
        FROM input_table, seq_0_to_3 AS seq
        WHERE seq.i < JSON_ARRAY_LENGTH(metadata)
      )
    SELECT *
    FROM exploded_array;
    
    Run Code Online (Sandbox Code Playgroud)

    生产:

     id | json
    ------------------------------
     1  | {"pet":"dog"}
     1  | {"country":"uk"}
     2  | {"pet":"cat"}
     4  | {"country":"germany"}
     4  | {"education":"masters"}
     4  | {"country":"belgium"}
    
    Run Code Online (Sandbox Code Playgroud)
  3. 但是,我需要提取字段名称/值。因为我看不到使用Redshift的有限函数提取JSON字段名称的任何方法,所以我将使用正则表达式来做到这一点:

    WITH exploded_array AS (                                                                                       
        SELECT id, JSON_EXTRACT_ARRAY_ELEMENT_TEXT(metadata, seq.i) AS json
        FROM input_table, seq_0_to_3 AS seq
        WHERE seq.i < JSON_ARRAY_LENGTH(metadata)
    )
    SELECT id, field, JSON_EXTRACT_PATH_TEXT(json, field)
    FROM (
        SELECT id, json, REGEXP_SUBSTR(json, '[^{"]\\w+[^"]') AS field
        FROM exploded_array
    );
    
    Run Code Online (Sandbox Code Playgroud)


Tom*_*ish 6

现在,Redshift 可以将数组格式 [] 或 json 格式 {} 中的字符串视为可解析的 json 结构。首先让我们根据您的数据创建一个临时表:

create temporary table #t1 (id int, json_str varchar(100));
truncate table #t1;
insert into #t1 values (1, '[{"pet":"dog"},{"country":"uk"}]');
insert into #t1 values (2, '[{"pet":"cat"}]');
insert into #t1 values (3, '[]');
insert into #t1 values (4, '[{"country":"germany"},{"education":"masters"},{"country":"belgium"}]');
Run Code Online (Sandbox Code Playgroud)

这个公共表表达式(cte)的创建将用于将 json_str 字段隐式转换为 SUPER 类型的正式 json 结构。如果表的字段已经是SUPER类型,我们可以跳过这一步。

drop table if exists #t2;
create temporary table #t2 as
with cte as 
    (select 
        x.id,
        json_parse(x.json_str) as json_str -- convert string to SUPER structure 
    from
        #t1 x
    )
select
    x.id
    ,unnested
from
    cte x, x.json_str as unnested -- an alias of cte and x.json_str is required!
order by 
    id
;
Run Code Online (Sandbox Code Playgroud)

现在我们有了一个可以轻松提取的键/值对的分解列表:

select 
    t2.id
    ,json_key -- this is the extracted key
    ,cast(json_val as varchar) as json_val -- eleminates the double quote marks
from
    #t2 t2, unpivot t2.unnested as json_val at json_key --"at some_label" (e.g. json_key) will extract the key
order by
    id
Run Code Online (Sandbox Code Playgroud)

呈现信息的另一种方法是允许解析引擎将键转换为列。这不是您所要求的,但可能很有趣:

select 
    id
    ,cast(t2.unnested.country as varchar) -- data is already parsed into rows, so it's directly addressable now
    ,cast(t2.unnested.education as varchar)
    ,cast(t2.unnested.pet as varchar)
from
    #t2 t2
;
Run Code Online (Sandbox Code Playgroud)

如果您想了解更多信息,请使用搜索引擎搜索解析 SUPER 数据类型。如果数据已作为 SUPER 存在于 Redshift 表中,则后两个查询将针对该表本机运行,无需临时表。


小智 5

有通用版本CREATE VIEW seq_0_to_3。我们就这样称呼它吧CREATE VIEW seq_0_to_n。这可以通过以下方式生成

CREATE VIEW seq_0_to_n AS (  
    SELECT row_number() over (
                          ORDER BY TRUE)::integer - 1 AS i
    FROM <insert_large_enough_table> LIMIT <number_less_than_table_entries>);
Run Code Online (Sandbox Code Playgroud)

这有助于生成大序列作为视图。