如何从动态查询中插入具有多列的table?
How to INSERT INTO table having multiple column from dynamic query?
类似于 的一对一映射,我需要一个针对源和目标中多列的解决方案。
仍在使用 Postgres 9.4.4,查询和模式已修改如下:
假设我有这两个 tables Table1
和 Table2
:
Create table Table1(col1 int, col2 varchar(100), col3 varchar(100));
Create table Table2(col1 int, col2 varchar(100), col3 varchar(100));
还有一个table 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);
如何在动态查询中编译此公式并插入到目标中table?
为多列动态构建语句的基本查询 - 忽略 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"
这假设一个单一来源和一个单一 目的地 table。或者事情变得更复杂。我添加了 WHERE
条件来说明这一点。我添加到 的区分大小写的注释仍然适用。
上面还是忽略了condition
。首先,不要在 condition
列中包含关键字 WHERE
。这只是噪音,没有帮助:
INSERT INTO Table3(tbl_src, col_src, tbl_des, col_des, condition)
VALUES ('Table1','col1','Table2','col1', <b>'col1>=1'</b>) -- without WHERE!
, ('Table1', ...
警告
这种方法本质上是不安全的。 condition
包含需要“按原样”连接的表达式,因此您完全可以接受 SQL 注入攻击 。您需要确保不受信任的用户不能以任何方式写入 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
子句中添加通用条件。
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;
条件是 AND 运算,并且 WHERE
子句仅在存在任何条件时附加 - 否则生成的 NULL 值会吞没表达式 ' WHERE ' || string_agg(condition, ' AND '))
中添加的关键字
在 DO
命令或 plpgsql 函数中使用它以按照我之前的回答中的指示动态执行:
基本plpgsql函数:
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;
类似于
仍在使用 Postgres 9.4.4,查询和模式已修改如下:
假设我有这两个 tables Table1
和 Table2
:
Create table Table1(col1 int, col2 varchar(100), col3 varchar(100));
Create table Table2(col1 int, col2 varchar(100), col3 varchar(100));
还有一个table 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);
如何在动态查询中编译此公式并插入到目标中table?
为多列动态构建语句的基本查询 - 忽略 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"
这假设一个单一来源和一个单一 目的地 table。或者事情变得更复杂。我添加了 WHERE
条件来说明这一点。我添加到
上面还是忽略了condition
。首先,不要在 condition
列中包含关键字 WHERE
。这只是噪音,没有帮助:
INSERT INTO Table3(tbl_src, col_src, tbl_des, col_des, condition)
VALUES ('Table1','col1','Table2','col1', <b>'col1>=1'</b>) -- without WHERE!
, ('Table1', ...
警告
这种方法本质上是不安全的。 condition
包含需要“按原样”连接的表达式,因此您完全可以接受 SQL 注入攻击 。您需要确保不受信任的用户不能以任何方式写入 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
子句中添加通用条件。
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;
条件是 AND 运算,并且 WHERE
子句仅在存在任何条件时附加 - 否则生成的 NULL 值会吞没表达式 ' WHERE ' || string_agg(condition, ' AND '))
在 DO
命令或 plpgsql 函数中使用它以按照我之前的回答中的指示动态执行:
基本plpgsql函数:
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;