PostgreSQL - 列名上的动态 INSERT
PostgreSQL - dynamic INSERT on column names
我希望在 PostgreSQL 中动态地将一组列从一个 table 插入到另一个。我想我想做的是阅读 'checklist' 列标题(那些存在于 table 1 - 存储 table 中的列),如果它们存在于导出中table (table 2) 然后从 table 1 中一次性插入它们。 Table 2 将在其列中可变 - 一旦导入生病删除它并导入新的要导入的数据可能具有不同的列结构。所以我需要根据列名导入它。
例如
Table 1. - 存储 table
ID NAME YEAR LITH_AGE PROV_AGE SIO2 TIO2 CAO MGO COMMENTS
1 John 1998 2000 3000 65 10 5 5 comment1
2 Mark 2005 2444 3444 63 8 2 3 comment2
3 Luke 2001 1000 1500 77 10 2 2 comment3
Table 2.-导出table
ID NAME MG# METHOD SIO2 TIO2 CAO MGO
1 Amy 4 Method1 65 10 5 5
2 Poe 3 Method2 63 8 2 3
3 Ben 2 Method3 77 10 2 2
如您所见,导出 table 可能包含存储 table 中不存在的列,因此这些将被忽略。
我想一次插入所有这些列,正如我发现的那样,如果我按列单独插入,每次插入时都会扩展行数(也许有人可以解决这个问题?目前我我编写了一个函数来检查 table 2 中是否存在列名,如果存在,则将其插入,但正如所说,这每次都会扩展 table 的行,并将其余列设为 NULL ).
我函数中的 INSERT 行:
EXECUTE format('INSERT INTO %s (%s) (SELECT %s::%s FROM %s);',_tbl_import, _col,_col,_type,_tbl_export);
作为我的问题的一种 'code example':
EXECUTE FORMAT('INSERT INTO table1 (%s) (SELECT (%s) FROM table2)',columns)
其中 'columns' 将是一些变量,表示需要进入存储 table 的导出 table 中存在的列。这将是可变的,因为 table 2 每次都会不同。
理想情况下,这会将 Table 1 更新为:
ID NAME YEAR LITH_AGE PROV_AGE SIO2 TIO2 CAO MGO COMMENTS
1 John 1998 2000 3000 65 10 5 5 comment1
2 Mark 2005 2444 3444 63 8 2 3 comment2
3 Luke 2001 1000 1500 77 10 2 2 comment3
4 Amy NULL NULL NULL 65 10 5 5 NULL
5 Poe NULL NULL NULL 63 8 2 3 NULL
6 Ben NULL NULL NULL 77 10 2 2 NULL
更新的答案
由于我的原始答案后来不符合要求,但被要求 post information_schema 解决方案的替代示例,所以这里是。
我做了两个版本的解决方案:
V1 - 等同于已经给出的使用 information_schema 的示例。但该解决方案依赖于 table1 列 DEFAULTs。意思是,如果在 table2 处不存在的 table1 列没有 DEFAULT NULL 那么它将填充为默认值。
V2-修改为在两个table列不匹配的情况下强制'NULL'并且不继承table1 拥有 DEFAULT
版本 1:
CREATE OR REPLACE FUNCTION insert_into_table1_v1()
RETURNS void AS $main$
DECLARE
columns text;
BEGIN
SELECT string_agg(c1.attname, ',')
INTO columns
FROM pg_attribute c1
JOIN pg_attribute c2
ON c1.attrelid = 'public.table1'::regclass
AND c2.attrelid = 'public.table2'::regclass
AND c1.attnum > 0
AND c2.attnum > 0
AND NOT c1.attisdropped
AND NOT c2.attisdropped
AND c1.attname = c2.attname
AND c1.attname <> 'id';
-- Following is the actual result of query above, based on given data examples:
-- -[ RECORD 1 ]----------------------
-- string_agg | name,si02,ti02,cao,mgo
EXECUTE format(
' INSERT INTO table1 ( %1$s )
SELECT %1$s
FROM table2
',
columns
);
END;
$main$ LANGUAGE plpgsql;
版本 2:
CREATE OR REPLACE FUNCTION insert_into_table1_v2()
RETURNS void AS $main$
DECLARE
t1_cols text;
t2_cols text;
BEGIN
SELECT string_agg( c1.attname, ',' ),
string_agg( COALESCE( c2.attname, 'NULL' ), ',' )
INTO t1_cols,
t2_cols
FROM pg_attribute c1
LEFT JOIN pg_attribute c2
ON c2.attrelid = 'public.table2'::regclass
AND c2.attnum > 0
AND NOT c2.attisdropped
AND c1.attname = c2.attname
WHERE c1.attrelid = 'public.table1'::regclass
AND c1.attnum > 0
AND NOT c1.attisdropped
AND c1.attname <> 'id';
-- Following is the actual result of query above, based on given data examples:
-- t1_cols | t2_cols
-- --------------------------------------------------------+--------------------------------------------
-- name,year,lith_age,prov_age,si02,ti02,cao,mgo,comments | name,NULL,NULL,NULL,si02,ti02,cao,mgo,NULL
-- (1 row)
EXECUTE format(
' INSERT INTO table1 ( %s )
SELECT %s
FROM table2
',
t1_cols,
t2_cols
);
END;
$main$ LANGUAGE plpgsql;
还有 link 关于 pg_attribute table 列的文档,如果不清楚的话:https://www.postgresql.org/docs/current/static/catalog-pg-attribute.html
希望这对您有所帮助:)
我希望在 PostgreSQL 中动态地将一组列从一个 table 插入到另一个。我想我想做的是阅读 'checklist' 列标题(那些存在于 table 1 - 存储 table 中的列),如果它们存在于导出中table (table 2) 然后从 table 1 中一次性插入它们。 Table 2 将在其列中可变 - 一旦导入生病删除它并导入新的要导入的数据可能具有不同的列结构。所以我需要根据列名导入它。
例如
Table 1. - 存储 table
ID NAME YEAR LITH_AGE PROV_AGE SIO2 TIO2 CAO MGO COMMENTS
1 John 1998 2000 3000 65 10 5 5 comment1
2 Mark 2005 2444 3444 63 8 2 3 comment2
3 Luke 2001 1000 1500 77 10 2 2 comment3
Table 2.-导出table
ID NAME MG# METHOD SIO2 TIO2 CAO MGO
1 Amy 4 Method1 65 10 5 5
2 Poe 3 Method2 63 8 2 3
3 Ben 2 Method3 77 10 2 2
如您所见,导出 table 可能包含存储 table 中不存在的列,因此这些将被忽略。
我想一次插入所有这些列,正如我发现的那样,如果我按列单独插入,每次插入时都会扩展行数(也许有人可以解决这个问题?目前我我编写了一个函数来检查 table 2 中是否存在列名,如果存在,则将其插入,但正如所说,这每次都会扩展 table 的行,并将其余列设为 NULL ). 我函数中的 INSERT 行:
EXECUTE format('INSERT INTO %s (%s) (SELECT %s::%s FROM %s);',_tbl_import, _col,_col,_type,_tbl_export);
作为我的问题的一种 'code example':
EXECUTE FORMAT('INSERT INTO table1 (%s) (SELECT (%s) FROM table2)',columns)
其中 'columns' 将是一些变量,表示需要进入存储 table 的导出 table 中存在的列。这将是可变的,因为 table 2 每次都会不同。
理想情况下,这会将 Table 1 更新为:
ID NAME YEAR LITH_AGE PROV_AGE SIO2 TIO2 CAO MGO COMMENTS
1 John 1998 2000 3000 65 10 5 5 comment1
2 Mark 2005 2444 3444 63 8 2 3 comment2
3 Luke 2001 1000 1500 77 10 2 2 comment3
4 Amy NULL NULL NULL 65 10 5 5 NULL
5 Poe NULL NULL NULL 63 8 2 3 NULL
6 Ben NULL NULL NULL 77 10 2 2 NULL
更新的答案
由于我的原始答案后来不符合要求,但被要求 post information_schema 解决方案的替代示例,所以这里是。
我做了两个版本的解决方案:
V1 - 等同于已经给出的使用 information_schema 的示例。但该解决方案依赖于 table1 列 DEFAULTs。意思是,如果在 table2 处不存在的 table1 列没有 DEFAULT NULL 那么它将填充为默认值。
V2-修改为在两个table列不匹配的情况下强制'NULL'并且不继承table1 拥有 DEFAULT
版本 1:
CREATE OR REPLACE FUNCTION insert_into_table1_v1()
RETURNS void AS $main$
DECLARE
columns text;
BEGIN
SELECT string_agg(c1.attname, ',')
INTO columns
FROM pg_attribute c1
JOIN pg_attribute c2
ON c1.attrelid = 'public.table1'::regclass
AND c2.attrelid = 'public.table2'::regclass
AND c1.attnum > 0
AND c2.attnum > 0
AND NOT c1.attisdropped
AND NOT c2.attisdropped
AND c1.attname = c2.attname
AND c1.attname <> 'id';
-- Following is the actual result of query above, based on given data examples:
-- -[ RECORD 1 ]----------------------
-- string_agg | name,si02,ti02,cao,mgo
EXECUTE format(
' INSERT INTO table1 ( %1$s )
SELECT %1$s
FROM table2
',
columns
);
END;
$main$ LANGUAGE plpgsql;
版本 2:
CREATE OR REPLACE FUNCTION insert_into_table1_v2()
RETURNS void AS $main$
DECLARE
t1_cols text;
t2_cols text;
BEGIN
SELECT string_agg( c1.attname, ',' ),
string_agg( COALESCE( c2.attname, 'NULL' ), ',' )
INTO t1_cols,
t2_cols
FROM pg_attribute c1
LEFT JOIN pg_attribute c2
ON c2.attrelid = 'public.table2'::regclass
AND c2.attnum > 0
AND NOT c2.attisdropped
AND c1.attname = c2.attname
WHERE c1.attrelid = 'public.table1'::regclass
AND c1.attnum > 0
AND NOT c1.attisdropped
AND c1.attname <> 'id';
-- Following is the actual result of query above, based on given data examples:
-- t1_cols | t2_cols
-- --------------------------------------------------------+--------------------------------------------
-- name,year,lith_age,prov_age,si02,ti02,cao,mgo,comments | name,NULL,NULL,NULL,si02,ti02,cao,mgo,NULL
-- (1 row)
EXECUTE format(
' INSERT INTO table1 ( %s )
SELECT %s
FROM table2
',
t1_cols,
t2_cols
);
END;
$main$ LANGUAGE plpgsql;
还有 link 关于 pg_attribute table 列的文档,如果不清楚的话:https://www.postgresql.org/docs/current/static/catalog-pg-attribute.html
希望这对您有所帮助:)