How to INSERT INTO table having multiple column from dynamic query?

前端 未结 1 1689
栀梦
栀梦 2021-01-29 00:01

Similar to this previous question for a one-to-one mapping I need a solution for multiple columns in source and destination.
Still working with Postgres 9.4.4, the query and

1条回答
  •  走了就别回头了
    2021-01-29 00:30

    The basic query to build the statement for multiple columns dynamically - ignoring the condition column:

    SELECT format(
          'INSERT INTO %I (%s) SELECT %s FROM %I'
         , tbl_des
         , string_agg(quote_ident(col_des), ', ')
         , string_agg(quote_ident(col_src), ', ')
         , tbl_src) AS sql
    FROM   table3
    WHERE  tbl_des = 'Table2'
    AND    tbl_src = 'Table1'
    GROUP  BY tbl_des, tbl_src;
    

    Result:

    INSERT INTO "Table2" (col1, col2, col3)
    SELECT CASE col1, col2, col3 FROM "Table1"
    

    This assumes a single source and a single destination table. Or things get more complicated. I added WHERE conditions to make that clear. The notes on case-sensitivity I added to my previous answer still apply.

    The above still ignores the condition. First of all, don't include the keyword WHERE in the condition column. That's just noise and not helpful:

    INSERT INTO Table3(tbl_src, col_src, tbl_des, col_des, condition)
    VALUES ('Table1','col1','Table2','col1', 'col1>=1')  -- without WHERE!
         , ('Table1', ...

    Warning

    This approach is inherently unsafe. The condition holds expressions that need to be concatenated "as is", so you are completely open to SQL injection attacks. You need to make sure that untrusted users cannot write to table3 in any way to avoid this.

    Building on this, and assuming each condition shall apply to its respective column only, we can solve it by wrapping the column in a CASE expression:

    SELECT format(
          'INSERT INTO %I (%s) SELECT %s FROM %I'
         , tbl_des
         , string_agg(quote_ident(col_des), ', ')
         , string_agg(
             CASE WHEN condition IS NULL
                THEN quote_ident(col_src)
                ELSE format('CASE WHEN %s THEN %I END'
                     , condition, col_src)  -- condition is unsafe!
             END, ', ')
         , tbl_src) AS sql
    FROM   table3
    WHERE  tbl_des = 'Table2'
    AND    tbl_src = 'Table1'
    GROUP  BY tbl_des, tbl_src;
    

    Produces a statement of the form:

    INSERT INTO "Table2" (col1, col2, col3)
    SELECT CASE WHEN col1>=1 THEN col1 END, col2, col3 FROM "Table1"
    

    Or, like you added in a later comment, conditions can apply to whole rows. Logically, this is another grey area. The condition is stored with a particular column, but applies to the whole row ...

    Be that as it may, you can add universal conditions in a WHERE clause.

    SELECT format(
          'INSERT INTO %I (%s) SELECT %s FROM %I%s'
         , tbl_des
         , string_agg(quote_ident(col_des), ', ')
         , string_agg(quote_ident(col_src), ', ')
         , tbl_src
         , ' WHERE ' || string_agg(condition, ' AND ')) AS sql
    FROM   table3
    WHERE  tbl_des = 'Table2'
    AND    tbl_src = 'Table1'
    GROUP  BY tbl_des, tbl_src;
    

    Conditions are ANDed, and the WHERE clause is only appended if there are any conditions - else the resulting NULL values swallows the added keyword in the expression ' WHERE ' || string_agg(condition, ' AND '))

    Use this in a DO command or plpgsql function to execute dynamically like instructed in my previous answer:

    • How to INSERT INTO table from dynamic query?

    Basic plpgsql function:

    CREATE OR REPLACE FUNCTION f_test()
      RETURNS void AS
    $func$
    BEGIN
       EXECUTE (
       SELECT format(
             'INSERT INTO %I (%s) SELECT %s FROM %I%s'
            , tbl_des
            , string_agg(quote_ident(col_des), ', ')
            , string_agg(quote_ident(col_src), ', ')
            , tbl_src
            , ' WHERE ' || string_agg(condition, ' AND ')) AS sql
       FROM   table3
       WHERE  tbl_des = 'Table2'
       AND    tbl_src = 'Table1'
       GROUP  BY tbl_des, tbl_src
       );
    END
    $func$ LANGUAGE plpgsql;
    

    SQL Fiddle.

    0 讨论(0)
提交回复
热议问题