如何在动态查询中插入包含多列的表格?

时间:2015-12-10 10:25:53

标签: sql postgresql dynamic-sql

this previous question类似,对于一对一的映射,我需要一个源和目标中多列的解决方案。
仍在使用Postgres 9.4.4,修改了查询和模式,如下所示:

假设我有两个表Table1Table2

Create table Table1(col1 int, col2 varchar(100), col3 varchar(100));
Create table Table2(col1 int, col2 varchar(100), col3 varchar(100));

另一个表格Table3存储了将数据从Table1迁移到Table2的公式:

CREATE TABLE Table3 (     
  tbl_src character varying(200),
  col_src character varying(500),
  tbl_des character varying(200),
  col_des character varying(100),
  condition character varying(500)
);

Insert into Table3(tbl_src, col_src, tbl_des, col_des, condition)
VALUES ('Table1','col1','Table2','col1', 'WHERE col1>=1')
     , ('Table1','col2','Table2','col2', NULL)
     , ('Table1','col3','Table2','col3', NULL);

如何在动态查询中编译此公式并插入目标表?

1 个答案:

答案 0 :(得分:0)

动态构建多列的语句的基本查询 - 忽略condition列:

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;

结果:

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

这假定 来源和 目标表。或者事情变得更复杂。我添加了WHERE条件以明确这一点。我添加到my previous answer的区分大小写的注释仍然适用。

以上仍然忽略了condition。首先,请勿在{{1​​}}列中添加关键字WHERE。这只是噪音而没有帮助:

condition

警告

这种方法本质上是不安全的。 INSERT INTO Table3(tbl_src, col_src, tbl_des, col_des, condition) VALUES ('Table1','col1','Table2','col1', 'col1>=1') -- without WHERE! , ('Table1', ...包含需要“按原样”连接的表达式,因此您完全可以接受 SQL注入攻击。您需要确保不受信任的用户无法以任何方式写入condition以避免这种情况。

在此基础上,假设每个条件仅适用于其各自的列,我们可以通过将列包装在table3表达式中来解决它:

CASE

生成表格声明:

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;

或者,就像您在稍后的评论中添加的那样,条件可以应用于整行。从逻辑上讲,这是另一个灰色区域。条件存储在特定列中,但适用于整行...

尽管如此,您可以在INSERT INTO "Table2" (col1, col2, col3) SELECT CASE WHEN col1>=1 THEN col1 END, col2, col3 FROM "Table1" 子句中添加通用条件。

WHERE

条件是AND,并且只有在有条件的情况下才附加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; 子句 - 否则生成的NULL值会吞下表达式WHERE中的已添加关键字

' WHERE ' || string_agg(condition, ' AND '))命令或plpgsql函数中使用它,就像我之前的答案中指示的那样动态执行:

基本plpgsql函数:

DO

SQL Fiddle.