最有效的psycopg2 python转义功能

时间:2014-07-05 19:11:48

标签: python postgresql python-3.x psycopg2

我目前正在尝试使用psycopy2进行批量插入(copy_from)。目前我的代码如下。我希望得到一些关于最有效的转义函数和转义包含受限字符的值的最佳实践的建议。我正在使用python3,字符串来了mongodb。

此处引用受限制的字符: http://www.postgresql.org/docs/9.2/static/sql-copy.html 从文档中可以看出: “反斜杠字符()可以在COPY数据中用于引用可能被视为行或列分隔符的数据字符。特别是,如果以下字符作为列值的一部分出现,则必须以反斜杠开头:反斜杠本身,换行符,回车符和当前分隔符。“

def bulk_write(self, table, data, columns = None):
    with psycopg2.connect(database = self.database,
                          user = self.user,
                          host = self.host,
                          password = self.password) as conn:
      with conn.cursor() as cur:
        cur.execute("SET TIME ZONE 'PDT8PST';")
        cols_import = tuple(columns) if columns else None
        data_tsv = '\n'.join(['\t'.join(self.escape_copy_string(str(value)) for value in row) for row in data])
        with open("test","w") as f:
          f.write(data_tsv)
        cur.copy_from(io.StringIO(data_tsv), table, columns=cols_import, null="None")

   def escape_copy_string(self,s):
      s = s.replace("\\","\\\\").replace("\n","\\n").replace("\r","\\r").replace("\t","\\t")
      return s

2 个答案:

答案 0 :(得分:1)

我建议使用csv模块,并在copy_from模式下使用csv,而不是手动滚动它。

如果您使用列表推导来处理数据,那么数据也不会那么大。你可以用大量的东西匆忙地用完RAM。考虑使用一个循环来代替每行写入。

答案 1 :(得分:0)

经过一些测试,我认为使用 replace 是最有效的,至少与基于正则表达式的方法相比:

import re
import timeit

string_rep = {
    '\\': '\\\\',
    '\r': '\\r',
    '\n': '\\n',
    '\t': '\\t',
}
string_pattern = re.compile('|'.join(re.escape(key) for key in string_rep))


def escape_re_sub(text):
    return string_pattern.sub(lambda m: string_rep[m.group(0)], text)


def escape_str_replace(text):
    return (
        text.replace('\\', '\\\\')
        .replace('\n', '\\n')
        .replace('\r', '\\r')
        .replace('\t', '\\t')
    )


string_no_escape = 'This is some bit of text that has no strings to replace'

time_re_sub = timeit.Timer(lambda: escape_re_sub(string_no_escape)).autorange()
print('no escape sub    ', time_re_sub[0] / time_re_sub[1], 'iterations per second')

time_str_replace = timeit.Timer(lambda: escape_str_replace(string_no_escape)).autorange()
print('no escape replace', time_str_replace[0] / time_str_replace[1], 'iterations per second')

string_escape = 'This is some\r \\ bit of text \nthat has \t\t some things to \\t replace'

time_re_sub = timeit.Timer(lambda: escape_re_sub(string_escape)).autorange()
print('with escape sub    ', time_re_sub[0] / time_re_sub[1], 'iterations per second')

time_str_replace = timeit.Timer(lambda: escape_str_replace(string_escape)).autorange()
print('with escape replace', time_str_replace[0] / time_str_replace[1], 'iterations per second')

对我来说,输出:

no escape sub     1088292.3082792824 iterations per second
no escape replace 1310796.652683603 iterations per second
with escape sub     251530.53121228397 iterations per second
with escape replace 913308.513839589 iterations per second

当有字符要转义时,差异显得特别明显,但在转义不改变字符串时存在。