我是一个python新手,所以如果有一个我没有看到的简单解决方案,请原谅我。我有大量的txt文件(~280)我需要读入postgres数据库中的表。该表的结构使其包含txt文件中存在的所有可能的列/变量。考虑下面的例子......
file1.txt
:
ID | Var_A | Var_B
1 | Apple | Dog
2 | Peach | Bird
file2.txt
:
ID | Var_A | Var_C
5 | Grape | Cat
3 | Apple | Dog
file3.txt
:
ID | Var_B | Var_C
7 | Apple | Cat
6 | Peach | Bird
理想情况下,我希望我的表能够包含txt文件中的所有数据,其中txt文件中不存在的列/变量将被记录为空白或空。 (为了示范和清晰起见,我在下面将它们写为空。)
postgres_table:
ID | Var_A | Var_B | Var_C
1 | Apple | Dog | NULL
2 | Peach | Bird | NULL
3 | Apple | NULL | Dog
5 | Grape | NULL | Cat
6 | NULL | Peach | Bird
7 | NULL | Apple | Cat
鉴于文件数量很大,此过程需要自动化。我一直在python中使用psycopg2执行此任务,并且已经做了许多先前的尝试,但没有一个成功。我相信COPY
查询效率最高(有数百万行数据),但在尝试复制时,我在txt文件中的非现有列有困难。
cursor = db.cursor()
query = "COPY postgres_table FROM '{}'(DELIMITER('|'));".format(~/file1.txt)
cursor.execute(query)
db.commit()
此查询产生以下错误:
psycopg2.DataError: missing data for column "Var_C"
也许有人可以看到我不知道的。我也对这项任务的其他潜在解决方案感兴趣。
答案 0 :(得分:0)
一种选择是将文本文件作为字符串读取,然后修改字符串以有效地添加空值,然后将其传递给格式方法而不是文件。
因此,您可以读取文件的第一行,查看哪些字段缺失(将其与数据库表中的字段进行比较)并添加“|”在字符串的每一行中都有一个缺少的字段。
答案 1 :(得分:0)
您可以使用临时表将所有文件中的数据加载到其中,然后使用plpgsql
甚至plpython
执行您想要的操作。
例如:
-- Destination table
create table if not exists dest(
id int,
var_a varchar,
var_b varchar,
var_c varchar);
begin; -- Start transaction
-- Temporary table to load data from all files
create temporary table tt(
tt_order serial,
tt_id varchar,
tt_var_1 varchar,
tt_var_2 varchar,
tt_file varchar)
on commit drop; -- It will be drooped at the end of transaction
-- Load data, header will be included
copy tt(tt_id, tt_var_1, tt_var_2) from '~/file1.txt' (delimiter '|', header false);
update tt set tt_file = '~/file1.txt' where tt_file is null;
copy tt(tt_id, tt_var_1, tt_var_2) from '~/file2.txt' (delimiter '|', header false);
update tt set tt_file = '~/file2.txt' where tt_file is null;
copy tt(tt_id, tt_var_1, tt_var_2) from '~/file3.txt' (delimiter '|', header false);
update tt set tt_file = '~/file3.txt' where tt_file is null;
-- Remove spaces
update tt set
tt_id = trim(tt_id),
tt_var_1 = trim(tt_var_1),
tt_var_2 = trim(tt_var_2);
select * from tt;
-- Result is
/*
tt_order | tt_id | tt_var_1 | tt_var_2 | tt_file
----------+-------+----------+----------+-------------
1 | ID | Var_A | Var_B | ~/file1.txt
2 | 1 | Apple | Dog | ~/file1.txt
3 | 2 | Peach | Bird | ~/file1.txt
4 | ID | Var_A | Var_C | ~/file2.txt
5 | 5 | Grape | Cat | ~/file2.txt
6 | 3 | Apple | Dog | ~/file2.txt
7 | ID | Var_B | Var_C | ~/file3.txt
8 | 7 | Apple | Cat | ~/file3.txt
9 | 6 | Peach | Bird | ~/file3.txt
(9 rows)
*/
-- Move data to destination table
do language plpgsql $$
declare
r record;
q text;
begin
for r in select * from tt order by tt_order loop
if lower(r.tt_id) = 'id' then -- If header from next file
-- Build insert query
q := 'insert into dest (id, ' || lower(r.tt_var_1) || ', ' || lower(r.tt_var_2) || ') values ($1::int, $2, $3)';
else
-- Insert data record
execute q using r.tt_id, r.tt_var_1, r.tt_var_2;
end if;
end loop;
end; $$;
commit;
select * from dest;
-- Result is
/*
id | var_a | var_b | var_c
----+-------+-------+-------
1 | Apple | Dog |
2 | Peach | Bird |
5 | Grape | | Cat
3 | Apple | | Dog
7 | | Apple | Cat
6 | | Peach | Bird
(6 rows)
*/