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

时间:2015-05-12 15:48:31

标签: json amazon-redshift

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

 id | metadata
---------------------------------------------------------------------------
 1  | [{"pet":"dog"},{"country":"uk"}]
 2  | [{"pet":"cat"}]
 3  | []
 4  | [{"country":"germany"},{"education":"masters"},{"country":"belgium"}]
  • 所有数组元素只有一个字段。
  • 无法保证特定字段在任何数组元素中都具有特征。
  • 字段名称可以在数组中重复
  • 数组元素可以按任何顺序

我想要找回一个看起来像这样的表:

 id |   field   |  value
------------------------
 1  | pet       | dog
 1  | country   | uk
 2  | pet       | cat
 4  | country   | germany
 4  | education | masters
 4  | country   | belgium

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

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

如果我能澄清其他任何内容,请告诉我。

2 个答案:

答案 0 :(得分:5)

感谢blog post的启发,我已经能够制定出一个解决方案。这是:

  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          
    );
    
  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;
    

    产:

     id | json
    ------------------------------
     1  | {"pet":"dog"}
     1  | {"country":"uk"}
     2  | {"pet":"cat"}
     4  | {"country":"germany"}
     4  | {"education":"masters"}
     4  | {"country":"belgium"}
    
  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
    );
    

答案 1 :(得分:1)

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>);

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