我必须从几个不同的数据库引擎中提取数据。导出此数据后,我将数据发送到AWS S3并使用COPY命令将该数据复制到Redshift。某些表包含大量文本,列字段中包含换行符和其他字符。当我运行以下代码时:
cursor.execute('''SELECT * FROM some_schema.some_message_log''')
rows = cursor.fetchall()
with open('data.csv', 'w', newline='') as fp:
a = csv.writer(fp, delimiter='|', quoting=csv.QUOTE_ALL, quotechar='"', doublequote=True, lineterminator='\n')
a.writerows(rows)
一些具有回车符/换行符的列将创建新行:
"2017-01-05 17:06:32.802700"|"SampleJob"|""|"Date"|"error"|"Job.py"|"syntax error at or near ""from"" LINE 34: select *, SYSDATE, from staging_tops.tkabsences;
^
-<class 'psycopg2.ProgrammingError'>"
导致导入过程失败。我可以通过对异常进行硬编码来解决这个问题:
cursor.execute('''SELECT * FROM some_schema.some_message_log''')
rows = cursor.fetchall()
with open('data.csv', 'w', newline='') as fp:
a = csv.writer(fp, delimiter='|', quoting=csv.QUOTE_ALL, quotechar='"', doublequote=True, lineterminator='\n')
for row in rows:
list_of_rows = []
for c in row:
if isinstance(c, str):
c = c.replace("\n", "\\n")
c = c.replace("|", "\|")
c = c.replace("\\", "\\\\")
list_of_rows.append(c)
else:
list_of_rows.append(c)
a.writerow([x.encode('utf-8') if isinstance(x, str) else x for x in list_of_rows])
但这需要很长时间才能处理更大的文件,而且看起来一般都是不好的做法。是否有更快的方法将数据从SQL游标导出到CSV,在面对包含回车符/换行符的文本列时不会中断?
答案 0 :(得分:3)
如果您在没有SELECT * FROM table
条款的情况下执行WHERE
,则可以使用COPY table TO STDOUT
代替正确的选项:
copy_command = """COPY some_schema.some_message_log TO STDOUT
CSV QUOTE '"' DELIMITER '|' FORCE QUOTE *"""
with open('data.csv', 'w', newline='') as fp:
cursor.copy_expert(copy_command)
在我的测试中,这导致文字'\ n'而不是实际的换行符,其中通过csv编写器写入会产生断行。
如果在生产中确实需要WHERE
子句,您可以创建一个临时表并将其复制:
cursor.execute("""CREATE TEMPORARY TABLE copy_me AS
SELECT this, that, the_other FROM table_name WHERE conditions""")
(编辑)再次看你的问题,我看到你提到“所有不同的数据库引擎”。上面的内容适用于psyopg2和postgresql,但可能适用于其他数据库或库。
答案 1 :(得分:2)
我怀疑问题就像确保Python CSV导出库和Redshift的COPY导入说一个通用界面一样简单。简而言之,检查分隔符并引用字符,并确保Python输出和Redshift COPY命令都一致。
稍微详细一点:数据库驱动程序已经完成了以易于理解的形式进入Python的艰苦工作。也就是说,来自DB的每一行是列表(或元组,生成器等),并且每个单元都是可单独访问的。而且,当你有一个类似列表的结构时,Python的CSV导出器可以完成其余的工作 - 至关重要的是 - Redshift将能够从输出,嵌入的换行符和所有内容中复制。 特别是,您不需要进行任何手动转义;您应该只需要.writerow()
或.writerows()
个功能。
Redshift的COPY实现默认了解最常见的CSV方言,即
,
),"
),"
→""
)来转义任何嵌入的双引号。使用Redshift FORMAT AS CSV
的文档提供支持:
...默认引号字符是双引号(“)。当在字段中使用引号字符时,使用附加引号字符转义字符。...
但是,您的Python CSV导出代码使用管道(|
)作为delimiter
,并将quotechar
设置为双引号("
)。那也可以,但为什么偏离the defaults呢?建议使用CSV的同名并在此过程中简化代码:
cursor.execute('''SELECT * FROM some_schema.some_message_log''')
rows = cursor.fetchall()
with open('data.csv', 'w') as fp:
csvw = csv.writer( fp )
csvw.writerows(rows)
从那里,告诉COPY使用CSV格式(同样不需要非默认规格):
COPY your_table FROM your_csv_file auth_code FORMAT AS CSV;
应该这样做。
答案 2 :(得分:0)
为什么要在每一行之后写入数据库?
cursor.execute('''SELECT * FROM some_schema.some_message_log''')
rows = cursor.fetchall()
with open('data.csv', 'w', newline='') as fp:
a = csv.writer(fp, delimiter='|', quoting=csv.QUOTE_ALL, quotechar='"', doublequote=True, lineterminator='\n')
list_of_rows = []
for row in rows:
for c in row:
if isinstance(c, basestring):
c = c.replace("\n", "\\n")
c = c.replace("|", "\|")
c = c.replace("\\", "\\\\")
list_of_rows.append(row)
a.writerows([x.encode('utf-8') if isinstance(x, str) else x for x in list_of_rows])
答案 3 :(得分:0)
问题在于您使用Redshift clearInterval(currentState.someInterval)
命令及其默认参数,该参数使用管道作为分隔符(请参阅here和here)并要求转义换行符和文本字段中的管道(请参阅here和here)。但是,Python csv编写器只知道如何使用嵌入的换行符进行标准操作,即将它们保留为带引号的字符串。
幸运的是,Redshift COPY
命令也可以使用标准的CSV格式。将COPY
选项添加到CSV
命令gives you this behavior:
允许在输入数据中使用CSV格式。要自动转义分隔符,换行符和回车符,请将该字段括在QUOTE参数指定的字符中。默认引号字符是双引号(“)。当在字段中使用引号字符时,使用附加引号字符转义字符。”
这正是Python CSV编写器使用的方法,因此它应该处理您的问题。所以我的建议是使用如下代码创建一个标准的csv文件:
COPY
然后在Redshift中,将您的cursor.execute('''SELECT * FROM some_schema.some_message_log''')
rows = cursor.fetchall()
with open('data.csv', 'w', newline='') as fp:
a = csv.writer(fp) # no need for special settings
a.writerows(rows)
命令更改为like this {注意添加的COPY
标记:
CSV
或者,您可以继续手动转换字段以匹配Redshift的COPY命令的默认设置。 Python的COPY logdata
FROM 's3://mybucket/data/data.csv'
iam_role 'arn:aws:iam::0123456789012:role/MyRedshiftRole'
CSV;
本身不会为你做这件事,但你可能会加速你的代码,特别是对于大文件,如:
csv.writer
作为另一种选择,您可以尝试将查询数据导入带有cursor.execute('''SELECT * FROM some_schema.some_message_log''')
rows = cursor.fetchall()
with open('data.csv', 'w', newline='') as fp:
a = csv.writer(
fp,
delimiter='|', quoting=csv.QUOTE_ALL,
quotechar='"', doublequote=True, lineterminator='\n'
)
a.writerows(
c.replace("\\", "\\\\").replace("\n", "\\\n").replace("|", "\\|").encode('utf-8')
if isinstance(c, str)
else c
for row in rows
for c in row
)
的{{1}} DataFrame,在DataFrame中进行替换(一次一行),然后将表格写出来与pandas
。 Pandas拥有令人难以置信的快速csv代码,因此这可能会为您带来显着的加速。
更新:我刚注意到,最后我基本上重复了@ hunteke的回答。关键点(我第一次错过了)是你可能没有在当前的Redshift .from_sql
命令中使用.to_csv
参数;如果你添加它,这应该很容易。