PostgreSQL-对列名称的动态INSERT [英] PostgreSQL - dynamic INSERT on column names

查看:165
本文介绍了PostgreSQL-对列名称的动态INSERT的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找在PostgreSQL中将一组列从一个表动态插入到另一个表中的方法.我想我想做的是在列标题的检查表"(表1中的那些列-存储表)中读取的,如果它们在导出表中存在(表2),则将它们插入所有但是,表2会立即在表中的列中进行更改.一旦导入,表2就会被删除,并导入可能要使用不同列结构导入的新数据.所以我需要根据列名导入它.

例如

表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

表2--导出表

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     

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

我想一次插入所有这些列,因为我发现如果我按列分别进行操作,则每次插入时它都会扩展行数(也许有人可以解决此问题?目前,我已经编写了一个函数来检查表2中是否存在列名,如果存在,则将其插入,但是正如所说的,这每次都会扩展表的行,其余的列为NULL. 我的函数中的INSERT行:

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

作为我的问题的代码示例"类型:

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

其中"columns"是一些变量,表示需要导出到存储表中的导出表中存在的列.这将是可变的,因为表2每次都不同.

理想情况下,将表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  

解决方案

更新后的答案

由于我的原始答案不符合要求,后来又出来了,但被要求在此发布一个有关information_schema解决方案的替代示例.

我为解决方案制作了两个版本:

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

V2-已修改为在两个表列不匹配的情况下强制为'NULL',并且不继承 table1 自己的默认值

版本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;

如果不清楚,还链接到有关 pg_attribute 表列的文档: 解决方案

UPDATED answer

As my original answer did not meet requirement came out later but was asked to post an alternative example for information_schema solution so here it is.

I made two versions for solutions:

V1 - is equivalent to already given example using information_schema. But that solution relies on table1 column DEFAULTs. Meaning, if table1 column that does not exist at table2 does not have DEFAULT NULL then it will be filled with whatever the default is.

V2 - is modified to force 'NULL' in case of two table columns mismatch and does not inherit table1 own DEFAULTs

Version1:

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;

Version2:

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;

Also link to documentation about pg_attribute table columns if something is unclear: https://www.postgresql.org/docs/current/static/catalog-pg-attribute.html

Hopefully this helps :)

这篇关于PostgreSQL-对列名称的动态INSERT的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆