Python使用将表从postgres导出并导入到另一个postgres

cygmwpex  于 2023-01-12  发布在  Python
关注(0)|答案(1)|浏览(144)

我有两个postgres数据库,它们的模式相同,但模式不同。我正在编写一个python脚本,目标是从其中一个表中导出部分数据,然后将结果导入到不同数据库中的同一个表中(如select from A where f=123)。架构很大(它有很多不同类型的列,有的允许空,有的不允许空,有日期类型,有字符串字段,可以包含句子,伪查询和文件名),并且在表中可以有数千行。
我采取的方法是将数据从表导出到csv文件,然后将数据从csv文件导入到第二个数据库表。
我使用psycopg2 lib在Python中处理Postgres沿着使用csv lib读写csv文件。
我实现了第一个版本。问题是:行中的某些列为空,当我在python中读取表数据时,空字段具有None值,当字段被允许为null时,并且当字段不被允许为null时,值为""空字符串,并且当导出到csv时,所有值None""作为空字符串插入到csv文件中。例如,该行看起来像1234,,,,,1,,。当我试图将文件导入到postgres表中时,csv中的所有空值都被转换为null,并试图以这种方式插入。但是它失败了,因为不能是null的字段不接受这个值。下面你可以看到我的代码和代码后,我粘贴的改进,我没有避免这个问题。

import psycopg2
import csv

def export_table(filename, tablename):
    conn = psycopg2.connect(....)

    cur = conn.cursor()

    cur.execute(f'SELECT * FROM {tablename} where f=123')

    rows = cur.fetchall()

    with open(filename, 'w', newline='') as csvfile:
        writer = csv.writer(csvfile)
        for row in rows:
            writer.writerow(row)

    cur.close()
    conn.close()

def import_table(filename, tablename):

    conn = psycopg2.connect(..second db data)

    cur = conn.cursor()
    with open(filename, 'r') as csvfile:
        cur.copy_expert(
            f"COPY {tablename} FROM STDIN WITH (FORMAT CSV)",
            csvfile
        )

    conn.commit()
    cur.close()
    conn.close()

我尝试添加csv.QUOTE_MINIMALcsv.QUOTE_NONNUMERIC-它们没有帮助我。
因为我无法用这段代码导入数据,所以我尝试再做一件事。
我添加了一个手动引用功能:

def quote_field(field):
    if isinstance(field, str):
        if field == '':
            return '""'
        elif any(c in field for c in (',', '"', '\n')):
            return '"' + field.replace('"', '""') + '"'

    return field

并以如下方式更新了导入部件:

with open(filename, 'w', newline='') as csvfile:
    writer = csv.writer(csvfile, quoting=csv.QUOTE_NONE, quotechar='', escapechar='\\')
    for row in rows:
        writer.writerow([quote_field(field) for field in row])

我试着运行这段代码,它将空值粘贴到csv中作为"",而None值则作为空字段放置在csv中。因此,csv中的一行将看起来像1234,,,"","",,,,,"",,,,,,在某些情况下,这将成功地工作。数据导入正确。2但是有时由于某些原因,生成的csv文件根本没有导入或者只是部分导入。为了检查它,我尝试使用DataGrip从csv文件手动导入数据,对于某些数据,它也只是部分导入(比如1000行中有20行),有些数据根本没有导入。我检查了csv的有效性,他们是有效的。我认为有一个错误在一个导入部分,但我不知道它在哪里,为什么它是这样的行为。需要帮助。

zkure5ic

zkure5ic1#

创建表格:

create table csv_null(id integer not null, fld1 varchar);
insert into csv_null values (1, 'test'), (2, ''), (3, null), (4, 'cat');
create table csv_null_2 as select * from csv_null limit 0;
 \pset null
Null display is "NULL".
select * from csv_null;
 id | fld1 
----+------
  1 | test
  2 | 
  3 | NULL
  4 | cat

Python代码:

import io
import psycopg2
con = psycopg2.connect(dbname="test", host='localhost', user='postgres', port=5432)
buffer = io.StringIO()
cur = con.cursor()
cur.copy_expert('copy (select * from csv_null ) TO  STDOUT WITH CSV HEADER', buffer)

buffer.seek(0)
cur.copy_expert('copy csv_null_2 from  STDIN WITH CSV HEADER', buffer)
con.commit()

cur.execute("select * from csv_null_2")
cur.fetchall()
[(1, 'test'), (2, ''), (3, None), (4, 'cat')]

在psql中:

select * from csv_null_2 ;
 id | fld1 
----+------
  1 | test
  2 | 
  3 | NULL
  4 | cat

cur.copy_expert('copy (select * from csv_null ) TO STDOUT WITH CSV HEADER', buffer)将产生cur.copy_expert('copy csv_null_2 from STDIN WITH CSV HEADER', buffer)将正确使用的输出,而不是从一个上下文Python csv跳到Postgres COPY

更新

修改表格以包含NOT NULL列:

alter table csv_null add column fld2 varchar not null default '';
update csv_null set fld2 = 'not null' where id in (1,4);
alter table csv_null_2 add column fld2 varchar not null default '';
truncate csv_null_2;

Python代码:

buffer = io.StringIO()
cur.copy_expert('copy (select * from csv_null ) TO  STDOUT WITH CSV HEADER', buffer)
buffer.seek(0)
cur.copy_expert('copy csv_null_2 from  STDIN WITH CSV HEADER', buffer)
con.commit()

cur.execute("select * from csv_null_2")
cur.fetchall()
[(2, '', ''), (3, None, ''), (1, 'test', 'not null'), (4, 'cat', 'not null')]

psql中:

select * from csv_null_2 ;
 id | fld1 |   fld2   
----+------+----------
  2 |      | 
  3 | NULL | 
  1 | test | not null
  4 | cat  | not null

相关问题