简体   繁体   English

将 json 嵌套提取到 postgres 表中

[英]Nested json extraction into postgres table

I have used following query to parse and store json elements into table 'pl' 'test' table is used to store raw json.我使用以下查询来解析 json 元素并将其存储到表“pl”中,“test”表用于存储原始 json。

select     
           each_attribute ->> 'id' id,
           each_attribute ->> 'sd' sd,
           each_attribute ->> 'v' v
from       test
cross join json_array_elements(json_array) each_section
cross join json_array_elements(each_section -> 'firstt') each_attribute

I am able to view following json values using above query but not able to insert it into another table using json_populate_recordset.我可以使用上述查询查看以下 json 值,但无法使用 json_populate_recordset 将其插入到另一个表中。

Table definition I need to insert nested json into: id integer, character varying(6666), character varying(99999)表定义我需要将嵌套的 json 插入:id 整数、字符变化(6666)、字符变化(99999)

Table1(for above definition) should store value for key firstt Table2(for above definition) should store value for key secondt Table1(对于上述定义)应该存储键 firstt 的值 Table2(对于上述定义)应该存储键 secondt 的值

Json format: json格式:

{
 "firstt": [
    {
      "id": 1,
      "sd": "test3",
      "v": "2223"
    },
    {
      "id": 2,
      "sd": "test2",
      "v": "2222"
    }],
"secondt": [
    {
      "id": 1,
      "sd": "test3",
      "v": "2223"
    },
    {
      "id": 2,
      "sd": "test2",
      "v": "2222"
    }]
}

Please assist.请协助。 I have tried every possible thing from stackoverflow solutions but nothing is given for nested array like this for insertion.我已经尝试了 stackoverflow 解决方案中的所有可能的事情,但没有为像这样的嵌套数组提供任何用于插入的内容。

Adding code for dynamic query.为动态查询添加代码。 It does not work.这没用。 Error -'too few arguments for format'.错误 -“格式参数太少”。

do $$
DECLARE
my record;
tb_n varchar(50);
BEGIN
FOR my IN 
  SELECT json_object_keys(json_array) as t FROM test
LOOP
tb_n := my.t;

EXECUTE format($$ WITH tbl_record_arrays as(
     SELECT
     entries.*
     FROM
       test
     JOIN LATERAL json_each(json_array) as entries(tbl_name,tbl_data_arr) ON TRUE
)
INSERT INTO %I
    SELECT
     records.*
     FROM
        tbl_record_arrays
          JOIN LATERAL json_populate_recordset(null::%I,tbl_data_arr) records ON TRUE
     WHERE
      tbl_name = %I$$,tb_n);
END LOOP;
END;
$$;

To create a plpgsql function that dynamically inserts a json array for a specified key into a specified table, you can do:要创建一个将指定键的 json 数组动态插入指定表的 plpgsql 函数,您可以执行以下操作:

CREATE OR REPLACE FUNCTION dynamic_json_insert(key_name text,tbl text) RETURNS VOID AS $$
    BEGIN
    -- the $<tag>$ syntax allows for generating a multiline string
    EXECUTE format($sql$
    INSERT INTO %1$I
        SELECT
          entries.*
        FROM test
        JOIN LATERAL json_populate_recordset(null::%1$I,json_data -> $1) as entries ON TRUE;
    $sql$::text,tbl) USING dynamic_json_insert.key_name;
     END;

$$ LANGUAGE plpgsql
   VOLATILE --modifies data
   STRICT -- Returns NULL if any arguments are NULL
   SECURITY INVOKER; --Execute this function with the Role of the caller, rather than the Role that defined the function;

and call it like并称它为

SELECT dynamic_json_insert('firstt','table_1')

If you want to insert into multiple tables using multiple key value pairs you can make a plpgsql function that takes a variadic array of key,table pairs and then generate a single Common Table Expression (CTE) with all of the INSERTs in a single atomic statement.如果要使用多个键值对插入到多个表中,可以创建一个 plpgsql 函数,该函数采用键、表对的variadic数组,然后在单个原子语句中生成具有所有 INSERT 的单个通用表表达式 (CTE) .

First create a custom type:首先创建一个自定义类型:

CREATE TYPE table_key as (
    tbl_key text,
    relation regclass -- special type that refers to a Postgresql relation
);

Then define the function:然后定义函数:

CREATE OR REPLACE FUNCTION dynamic_json_insert(variadic table_keys table_key[]) RETURNS VOID AS $$
    DECLARE
      tbl_key_len integer = array_length(dynamic_json_insert.table_keys,1);
    BEGIN
        IF tbl_key_len > 0 THEN
        EXECUTE (
        --generates a single atomic insert CTE when there are multiple table_keys OR a single insert statement otherwise
        --the SELECT is enclosed in parenthesis because it generates a single text value which EXECUTE receives.
        SELECT
    
         --append WITH if We have more than 1 table_key (for CTE)
         CASE WHEN tbl_key_len > 1 THEN 'WITH ' ELSE '' END
         || string_agg(
          CASE
             WHEN
              --name the auxiliary statement and put it in parenthesis.
              is_aux THEN  format('%1$I as (%2$s)','ins_' || tk.tbl_key,stmt) || end_char
            ELSE stmt
          END,E'\n') || ';'
        FROM
          --unnest the table_keys argument and get its index (rn)
          unnest(dynamic_json_insert.table_keys) WITH ORDINALITY AS tk(tbl_key,relation,rn)
          -- the JOIN LATERAL here means "for each unnested table_key, generate the rows of the following subquery"
          JOIN LATERAL (
           SELECT
             rn < tbl_key_len is_aux,
             --we need a comma between auxiliary statements
             CASE WHEN rn = tbl_key_len - 1 THEN '' ELSE ',' END end_char,
            --dynamically generate INSERT statement
             format($sql$
             INSERT INTO %1$I
                SELECT
                  entries.*
                FROM test
                JOIN LATERAL json_populate_recordset(null::%1$I,json_data -> %2$L) as entries ON TRUE
             $sql$::text,tk.relation,tk.tbl_key) stmt
            ) stmts ON TRUE
        );
        END IF;
    END;
$$ LANGUAGE plpgsql
    VOLATILE --modifies data
    STRICT -- Returns NULL if any arguments are NULL
    SECURITY INVOKER; --Execute this function with the Role of the caller, rather than the Role that defined the function;

Then call the function like:然后像这样调用函数:

SELECT dynamic_json_insert(
  ('firstt','table_1'),
  ('secondt','table_2')
);

Because of the use of the variadic keyword, you can pass in each element of the array as an individual argument and Postgres will cast to the appropriate types automatically.由于使用了variadic关键字,您可以将数组的每个元素作为单独的参数传入,Postgres 将自动转换为适当的类型。

The generated/executed SQL for the above function call will be:上述函数调用生成/执行的 SQL 将是:

WITH ins_firstt as (
             INSERT INTO table_1
                SELECT
                  entries.*
                FROM test
                JOIN LATERAL json_populate_recordset(null::table_1,json_data -> 'firstt') as entries ON TRUE
             )

             INSERT INTO table_2
                SELECT
                  entries.*
                FROM test
                JOIN LATERAL json_populate_recordset(null::table_2,json_data -> 'secondt') as entries ON TRUE
             ;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM