Самая эффективная функция экранирования psycopg2 python

#python #postgresql #python-3.x #psycopg2

#python #postgresql #python-3.x #psycopg2

Вопрос:

В настоящее время я пытаюсь выполнить массовую вставку с помощью psycopy2, используя (copy_from). В настоящее время мой код приведен ниже. Я надеялся получить несколько советов о наиболее эффективной экранирующей функции и наилучшей практике экранирования значений, содержащих ограниченные символы. Я использую python3, строки поступают из mongodb.

Здесь приведены ссылки на символы с ограниченным доступом:http://www.postgresql.org/docs/9.2/static/sql-copy.html Как взято из документации: «Символы обратной косой черты () могут использоваться в КОПИРУЕМЫХ данных для заключения в кавычки символов данных, которые в противном случае могли бы использоваться в качестве разделителей строк или столбцов. В частности, перед следующими символами должна стоять обратная косая черта, если они появляются как часть значения столбца: сама обратная косая черта, перевод строки, возврат каретки и текущий символ-разделитель.»

 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
  

Ответ №1:

Вместо того, чтобы запускать ее вручную, я предлагаю использовать csv модуль и использовать copy_from в csv режиме.

Данные не могут быть такими большими, если вы также используете для их обработки представления списка. У вас в спешке закончится оперативная память, если вы будете делать это с чем-то большим. Рассмотрите возможность использования цикла, который записывает каждую строку по мере ее выполнения.

Комментарии:

1. csv-модуль psycopg2 или csv-модуль python? Я загружаю данные пакетами. Спасибо!

2. csv Модуль as в Python от @BradRuderman psycopg2 не имеет csv подмодуля.

3. Спасибо за информацию, модуль CSV был хорошей идеей, однако повторное использование — нет. Если данные были слишком большими, я должен был разделить и выполнить массовые вставки. Циклическое выполнение было бы ужасным для производительности при начальных вставках

4. @BradRuderman Я был неясен. Я имел в виду, что вы могли бы выполнять цикл ввода и записывать каждую строку в copy_from поток, а не делать отдельные вставки.

5. CSV-файл PSA python не будет корректно экранировать массивы на диалекте pgsql

Ответ №2:

После небольшого тестирования я думаю, что использование 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 somer \ bit of text nthat has tt 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
  

Разница кажется особенно сильной, когда есть символы, которые нужно экранировать, но присутствует, когда экранирование не изменяет строки.