【问题标题】:PostgreSQL - dynamic INSERT on column namesPostgreSQL - 列名上的动态插入
【发布时间】:2017-01-19 09:37:03
【问题描述】:

我希望在 PostgreSQL 中将一组列从一个表动态插入到另一个表中。我想我想做的是读入列标题的“清单”(表1中存在的那些列-存储表),如果它们存在于导出表(表2)中,则将它们全部插入立即从表 1 中。虽然表 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)

其中“列”是一些变量,表示导出表中存在的需要进入存储表的列。这将是可变的,因为表 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  

【问题讨论】:

    标签: postgresql plpgsql dynamic-sql


    【解决方案1】:

    更新答案

    由于我最初的答案不符合要求,但后来被要求发布 information_schema 解决方案的替代示例,所以在这里。

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

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

    V2 - 被修改为在两个表列不匹配的情况下强制为“NULL”并且不继承 table1 自己的 DEFAULTs

    版本 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 表列的文档:https://www.postgresql.org/docs/current/static/catalog-pg-attribute.html

    希望这会有所帮助:)

    【讨论】:

    • 对不起,我应该在上面(现在添加)指定为什么我需要在列名上执行此操作。 '表 2' 将经常被删除和更改,它本质上是我用于将数据导入表 1 的临时表。表 2 并不总是具有相同的列组合,所以我不能在这里做 NULL,因为我不知道它是什么结构会有。我只知道我在表 1 中“寻找”哪些列名,如果存在,请导入。
    • 确认,那就是另一回事了。我看到你已经有了答案:)
    猜你喜欢
    • 2018-09-14
    • 1970-01-01
    • 1970-01-01
    • 2021-09-22
    • 1970-01-01
    • 2016-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多