PostgreSQL - 列名上的动态 INSERT

PostgreSQL - dynamic INSERT on column names

我希望在 PostgreSQL 中动态地将一组列从一个 table 插入到另一个。我想我想做的是阅读 'checklist' 列标题(那些存在于 table 1 - 存储 table 中的列),如果它们存在于导出中table (table 2) 然后从 table 1 中一次性插入它们。 Table 2 将在其列中可变 - 一旦导入生病删除它并导入新的要导入的数据可能具有不同的列结构。所以我需要根据列名导入它。

例如

Table 1. - 存储 table

ID     NAME     YEAR     LITH_AGE    PROV_AGE    SIO2    TIO2    CAO    MGO   COMMENTS
1      John     1998     2000        3000        65      10      5      5     comment1
2      Mark     2005     2444        3444        63      8       2      3     comment2
3      Luke     2001     1000        1500        77      10      2      2     comment3

Table 2.-导出table

ID     NAME     MG#    METHOD    SIO2    TIO2    CAO    MGO
1      Amy      4      Method1   65      10      5      5    
2      Poe      3      Method2   63      8       2      3   
3      Ben      2      Method3   77      10      2      2     

如您所见,导出 table 可能包含存储 table 中不存在的列,因此这些将被忽略。

我想一次插入所有这些列,正如我发现的那样,如果我按列单独插入,每次插入时都会扩展行数(也许有人可以解决这个问题?目前我我编写了一个函数来检查 table 2 中是否存在列名,如果存在,则将其插入,但正如所说,这每次都会扩展 table 的行,并将其余列设为 NULL ). 我函数中的 INSERT 行:

EXECUTE format('INSERT INTO %s (%s) (SELECT %s::%s FROM %s);',_tbl_import, _col,_col,_type,_tbl_export);

作为我的问题的一种 'code example':

EXECUTE FORMAT('INSERT INTO table1 (%s) (SELECT (%s) FROM table2)',columns)

其中 'columns' 将是一些变量,表示需要进入存储 table 的导出 table 中存在的列。这将是可变的,因为 table 2 每次都会不同。

理想情况下,这会将 Table 1 更新为:

ID     NAME     YEAR     LITH_AGE    PROV_AGE    SIO2    TIO2    CAO    MGO   COMMENTS
1      John     1998     2000        3000        65      10      5      5     comment1
2      Mark     2005     2444        3444        63      8       2      3     comment2
3      Luke     2001     1000        1500        77      10      2      2     comment3
4      Amy      NULL     NULL        NULL        65      10      5      5     NULL
5      Poe      NULL     NULL        NULL        63      8       2      3     NULL   
6      Ben      NULL     NULL        NULL        77      10      2      2     NULL  

更新的答案

由于我的原始答案后来不符合要求,但被要求 post information_schema 解决方案的替代示例,所以这里是。

我做了两个版本的解决方案:

V1 - 等同于已经给出的使用 information_schema 的示例。但该解决方案依赖于 table1DEFAULTs。意思是,如果在 table2 处不存在的 table1 列没有 DEFAULT NULL 那么它将填充为默认值。

V2-修改为在两个table列不匹配的情况下强制'NULL'并且不继承table1 拥有 DEFAULT

版本 1:

CREATE OR REPLACE FUNCTION insert_into_table1_v1()
RETURNS void AS $main$

DECLARE
    columns text;

BEGIN

    SELECT  string_agg(c1.attname, ',')
    INTO    columns
    FROM    pg_attribute c1
    JOIN    pg_attribute c2
    ON      c1.attrelid = 'public.table1'::regclass
    AND     c2.attrelid = 'public.table2'::regclass
    AND     c1.attnum > 0
    AND     c2.attnum > 0
    AND     NOT c1.attisdropped
    AND     NOT c2.attisdropped
    AND     c1.attname = c2.attname
    AND     c1.attname <> 'id';

    --       Following is the actual result of query above, based on given data examples:
    --       -[ RECORD 1 ]----------------------
    --       string_agg | name,si02,ti02,cao,mgo

    EXECUTE format(
        '   INSERT INTO table1 ( %1$s )
            SELECT %1$s
            FROM table2
        ',
        columns
    );

END;
$main$ LANGUAGE plpgsql;

版本 2:

CREATE OR REPLACE FUNCTION insert_into_table1_v2()
RETURNS void AS $main$

DECLARE
    t1_cols text;
    t2_cols text;

BEGIN

    SELECT  string_agg( c1.attname, ',' ),
            string_agg( COALESCE( c2.attname, 'NULL' ), ',' )
    INTO    t1_cols,
            t2_cols
    FROM    pg_attribute c1
    LEFT JOIN    pg_attribute c2
    ON      c2.attrelid = 'public.table2'::regclass
    AND     c2.attnum > 0
    AND     NOT c2.attisdropped
    AND     c1.attname = c2.attname
    WHERE   c1.attrelid = 'public.table1'::regclass
    AND     c1.attnum > 0
    AND     NOT c1.attisdropped
    AND     c1.attname <> 'id';

    --       Following is the actual result of query above, based on given data examples:
    --                               t1_cols                         |                  t2_cols
    --       --------------------------------------------------------+--------------------------------------------
    --        name,year,lith_age,prov_age,si02,ti02,cao,mgo,comments | name,NULL,NULL,NULL,si02,ti02,cao,mgo,NULL
    --       (1 row)

    EXECUTE format(
        '   INSERT INTO table1 ( %s )
            SELECT %s
            FROM table2
        ',
        t1_cols,
        t2_cols
    );

END;
$main$ LANGUAGE plpgsql;

还有 link 关于 pg_attribute table 列的文档,如果不清楚的话:https://www.postgresql.org/docs/current/static/catalog-pg-attribute.html

希望这对您有所帮助:)