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

白昼怎懂夜的黑 提交于 2019-12-02 10:48:38
Erwin Brandstetter

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:

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!