从一个 table 中提取列值并修改后插入另一个
Extract column values from one table and insert with modifications into another
我创建了一个 PL/pgSQL 函数,它接受两个列名、一个“关系”和两个 table 名称。它在一个 table 中找到不同的行并将它们插入临时 table,删除任何具有空值的行,并将一列的所有值设置为 relation
。我有使用此功能的流程的第一部分。
create or replace function alt_edger(s text, v text, relation text, tbl text, tbl_src text)
returns void
language plpgsql as
$func$
begin
raise notice 's: %, v: %, tbl: %, tbl_src: %', s,v,tbl,tbl_src;
execute ('insert into '||tbl||' ("source", "target") select distinct "'||s||'","'||v||'" from '||tbl_src||'');
execute ('DELETE FROM '||tbl||' WHERE "source" IS null or "target" is null');
end
$func$;
执行如下:
-- create a temporary table and execute the function twice
drop table if exists temp_stack;
create temporary table temp_stack("label" text, "source" text, "target" text, "attr" text, "graph" text);
select alt_edger('x_x', 'y_y', ':associated_with', 'temp_stack','pg_check_table' );
select alt_edger('Document Number', 'x_x', ':documents', 'temp_stack','pg_check_table' );
select * from temp_stack;
请注意,我还没有使用 relation
。 INSERT
也应分配 relation
,但我无法弄清楚如何做到这一点以获得类似的东西:
label
source
target
attr
graph
:associated_with
638000
ARAS
:associated_with
202000
JASE
:associated_with
638010
JASE
:associated_with
638000
JASE
:associated_with
202100
JASE
:documents
A
638010
:documents
A
202000
:documents
A
202100
:documents
B
638000
:documents
A
638000
:documents
B
124004
:documents
B
202100
我的挑战是:
- 如何在
INSERT
中整合relation
?当我尝试使用 VALUES
和逗号分隔时,出现“select 附近的错误”。
- 如何在
relation
中允许以“:”开头的字符串?我很期待,冒号的包含在过去给我带来了挑战。
我该怎么做?或者有更好的方法吗?
玩具数据模型:
drop table if exists pg_check_table;
create temporary table pg_check_table("Document Number" text, x_x int, y_y text);
insert into pg_check_table values ('A',202000,'JASE'),
('A',202100,'JASE'),
('A',638010,'JASE'),
('A',Null,'JASE'),
('A',Null,'JASE'),
('A',202100,'JASE'),
('A',638000,'JASE'),
('A',202100,'JASE'),
('B',638000,'JASE'),
('B',202100,null),
('B',638000,'JASE'),
('B',null,'ARAS'),
('B',638000,'ARAS'),
('B',null,'ARAS'),
('B',638000,null),
('B',124004,null);
alter table pg_check_table add row_num serial;
select * from pg_check_table;
-- DROP FUNCTION alt_edger(_s text, _v text, _relation text, _tbl text, _tbl_src text)
CREATE OR REPLACE FUNCTION alt_edger(_s text, _v text, _relation text, _tbl text, _tbl_src text, OUT row_count int)
LANGUAGE plpgsql AS
$func$
DECLARE
_sql text := format(
'INSERT INTO pg_temp.%3$I (label, source, target)
SELECT DISTINCT , %1$I, %2$I FROM pg_temp.%4$I
WHERE (%1$I, %2$I) IS NOT NULL'
, _s, _v, _tbl, _tbl_src);
BEGIN
-- RAISE NOTICE '%', _sql; -- debug
EXECUTE _sql USING _relation;
GET DIAGNOSTICS row_count = ROW_COUNT; -- return number of inserted rows
END
$func$;
db<>fiddle here
最重要的是,使用 format()
安全地连接您的动态 SQL 命令。并使用格式说明符 %I
作为标识符。这样,SQL 注入是不可能的,标识符是 double-quoted 正确 - 保留 non-standard 名称,如 Document Number
。那是你原来失败的地方。
我们 也可以 连接 _relation
作为要插入到 label
中的字符串。但是将 values 传递给 EXECUTE
的更好方法是使用 USING
子句。传递给 EXECUTE
的 SQL 字符串中的 </code> 是第一个 <code>USING
参数的占位符。不要与 </code> 在函数体 <em> 外部 </em> <code>EXECUTE
的上下文中引用函数参数混淆! (您可以传递 任何 字符串,前导冒号 (:
) 无关紧要,正确完成后不会解释该字符串。)
参见:
- Format specifier for integer variables in format() for EXECUTE?
- Table name as a PostgreSQL function parameter
我用 WHERE
子句替换了你原来的 DELETE
到 INSERT
的 SELECT
。不要一开始就插入行,而不是稍后再删除它们。
(%1$I, %2$I) IS NOT NULL
仅当两个值都为 NOT NULL
时才符合条件。
关于:
- Check if a Postgres composite field is null/empty
不要为您的 table 名称使用前缀“pg_”。这就是 Postgres 用于 system tables 的内容。别惹那些。
我 schema-qualify 已知临时 table 具有 pg_temp.
这通常是可选的,因为默认情况下临时模式在 search_path
中排在第一位。但这可以(恶意地)更改,然后 table 名称将解析为 search_path
中同名的任何现有常规 table。所以安全总比后悔好。参见:
- How does the search_path influence identifier resolution and the "current schema"
我将函数return设置为插入行数。这完全是可选的!
由于我使用 OUT
参数执行此操作,因此我可以跳过 RETURNS
子句。参见:
- Can I make a plpgsql function return an integer without using a variable?
我创建了一个 PL/pgSQL 函数,它接受两个列名、一个“关系”和两个 table 名称。它在一个 table 中找到不同的行并将它们插入临时 table,删除任何具有空值的行,并将一列的所有值设置为 relation
。我有使用此功能的流程的第一部分。
create or replace function alt_edger(s text, v text, relation text, tbl text, tbl_src text)
returns void
language plpgsql as
$func$
begin
raise notice 's: %, v: %, tbl: %, tbl_src: %', s,v,tbl,tbl_src;
execute ('insert into '||tbl||' ("source", "target") select distinct "'||s||'","'||v||'" from '||tbl_src||'');
execute ('DELETE FROM '||tbl||' WHERE "source" IS null or "target" is null');
end
$func$;
执行如下:
-- create a temporary table and execute the function twice
drop table if exists temp_stack;
create temporary table temp_stack("label" text, "source" text, "target" text, "attr" text, "graph" text);
select alt_edger('x_x', 'y_y', ':associated_with', 'temp_stack','pg_check_table' );
select alt_edger('Document Number', 'x_x', ':documents', 'temp_stack','pg_check_table' );
select * from temp_stack;
请注意,我还没有使用 relation
。 INSERT
也应分配 relation
,但我无法弄清楚如何做到这一点以获得类似的东西:
label | source | target | attr | graph |
---|---|---|---|---|
:associated_with | 638000 | ARAS | ||
:associated_with | 202000 | JASE | ||
:associated_with | 638010 | JASE | ||
:associated_with | 638000 | JASE | ||
:associated_with | 202100 | JASE | ||
:documents | A | 638010 | ||
:documents | A | 202000 | ||
:documents | A | 202100 | ||
:documents | B | 638000 | ||
:documents | A | 638000 | ||
:documents | B | 124004 | ||
:documents | B | 202100 |
我的挑战是:
- 如何在
INSERT
中整合relation
?当我尝试使用VALUES
和逗号分隔时,出现“select 附近的错误”。 - 如何在
relation
中允许以“:”开头的字符串?我很期待,冒号的包含在过去给我带来了挑战。
我该怎么做?或者有更好的方法吗?
玩具数据模型:
drop table if exists pg_check_table;
create temporary table pg_check_table("Document Number" text, x_x int, y_y text);
insert into pg_check_table values ('A',202000,'JASE'),
('A',202100,'JASE'),
('A',638010,'JASE'),
('A',Null,'JASE'),
('A',Null,'JASE'),
('A',202100,'JASE'),
('A',638000,'JASE'),
('A',202100,'JASE'),
('B',638000,'JASE'),
('B',202100,null),
('B',638000,'JASE'),
('B',null,'ARAS'),
('B',638000,'ARAS'),
('B',null,'ARAS'),
('B',638000,null),
('B',124004,null);
alter table pg_check_table add row_num serial;
select * from pg_check_table;
-- DROP FUNCTION alt_edger(_s text, _v text, _relation text, _tbl text, _tbl_src text)
CREATE OR REPLACE FUNCTION alt_edger(_s text, _v text, _relation text, _tbl text, _tbl_src text, OUT row_count int)
LANGUAGE plpgsql AS
$func$
DECLARE
_sql text := format(
'INSERT INTO pg_temp.%3$I (label, source, target)
SELECT DISTINCT , %1$I, %2$I FROM pg_temp.%4$I
WHERE (%1$I, %2$I) IS NOT NULL'
, _s, _v, _tbl, _tbl_src);
BEGIN
-- RAISE NOTICE '%', _sql; -- debug
EXECUTE _sql USING _relation;
GET DIAGNOSTICS row_count = ROW_COUNT; -- return number of inserted rows
END
$func$;
db<>fiddle here
最重要的是,使用 format()
安全地连接您的动态 SQL 命令。并使用格式说明符 %I
作为标识符。这样,SQL 注入是不可能的,标识符是 double-quoted 正确 - 保留 non-standard 名称,如 Document Number
。那是你原来失败的地方。
我们 也可以 连接 _relation
作为要插入到 label
中的字符串。但是将 values 传递给 EXECUTE
的更好方法是使用 USING
子句。传递给 EXECUTE
的 SQL 字符串中的 </code> 是第一个 <code>USING
参数的占位符。不要与 </code> 在函数体 <em> 外部 </em> <code>EXECUTE
的上下文中引用函数参数混淆! (您可以传递 任何 字符串,前导冒号 (:
) 无关紧要,正确完成后不会解释该字符串。)
参见:
- Format specifier for integer variables in format() for EXECUTE?
- Table name as a PostgreSQL function parameter
我用 WHERE
子句替换了你原来的 DELETE
到 INSERT
的 SELECT
。不要一开始就插入行,而不是稍后再删除它们。
(%1$I, %2$I) IS NOT NULL
仅当两个值都为 NOT NULL
时才符合条件。
关于:
- Check if a Postgres composite field is null/empty
不要为您的 table 名称使用前缀“pg_”。这就是 Postgres 用于 system tables 的内容。别惹那些。
我 schema-qualify 已知临时 table 具有 pg_temp.
这通常是可选的,因为默认情况下临时模式在 search_path
中排在第一位。但这可以(恶意地)更改,然后 table 名称将解析为 search_path
中同名的任何现有常规 table。所以安全总比后悔好。参见:
- How does the search_path influence identifier resolution and the "current schema"
我将函数return设置为插入行数。这完全是可选的!
由于我使用 OUT
参数执行此操作,因此我可以跳过 RETURNS
子句。参见:
- Can I make a plpgsql function return an integer without using a variable?