Как использовать SQLAlchemy для выгрузки файла SQL из выражений запросов для массовой вставки в СУБД? - PullRequest
12 голосов
/ 21 мая 2010

Пожалуйста, потерпите меня, пока я объясняю проблему, как я пытался ее решить, и мой вопрос о том, как его улучшить, в конце.

У меня есть 100 000 строк CSV-файла из автономного пакетного задания, и мне нужно было вставьте его в базу данных как соответствующие модели. Обычно, если это довольно прямолинейная загрузка, ее можно легко загрузить, просто подключив файл CSV для соответствия схеме; но мне пришлось выполнить некоторую внешнюю обработку, требующую запросов, и гораздо удобнее использовать SQLAlchemy для генерации нужных мне данных.

Данные, которые я хочу здесь, представляют собой 3 модели, которые представляют 3 предварительные таблицы в базе данных и каждая последующая модель зависит от предыдущей модели. Например:

Model C --> Foreign Key --> Model B --> Foreign Key --> Model A

Итак, модели должны быть вставлены в порядке A, B и C. Я придумал с подходом производителя / потребителя:

 - instantiate a multiprocessing.Process which contains a
 threadpool of 50 persister threads that have a threadlocal 
 connection to a database

 - read a line from the file using the csv DictReader

 - enqueue the dictionary to the process, where each thread creates
 the appropriate models by querying the right values and each
 thread persists the models in the appropriate order

Это было быстрее, чем многопоточное чтение / сохранение, но это намного медленнее, чем массовая загрузка файла в базу данных. Работа закончена сохраняющейся примерно через 45 минут . Ради интереса я решил написать это на SQL Заявления заняли 5 минут .

Написание операторов SQL заняло у меня пару часов. Так что мой вопрос, мог бы я использовать более быстрый метод для вставки строк с помощью SQLAlchemy? Насколько я понимаю, SQLAlchemy не предназначен для массовых операции вставки, так что это не идеально.

Это следует из моего вопроса, есть ли способ генерировать операторы SQL с использованием SQLAlchemy, throw их в файл, а затем просто использовать массовую загрузку в базу данных? я знать о str (model_object), но он не показывает интерполированный значения.

Буду признателен за любые рекомендации, как сделать это быстрее.

Спасибо!

Ответы [ 3 ]

3 голосов
/ 12 июня 2010

Обычно нет, нет способа получить запрос с включенными значениями.

Какую базу данных вы используете? Потому что во многих базах данных есть функция массовой загрузки для CSV.

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

'''Replace the parameter placeholders with values'''
params = compiler.params.items()
params.sort(key=lambda (k, v): len(str(k)), reverse=True)
for k, v in params:
    '''Some types don't need escaping'''
    if isinstance(v, (int, long, float, bool)):
        v = unicode(v)
    else:
        v = "'%s'" % v

    '''Replace the placeholders with values
    Works both with :1 and %(foo)s type placeholders'''
    query = query.replace(':%s' % k, v)
    query = query.replace('%%(%s)s' % k, v)
2 голосов
/ 10 июля 2010

Во-первых, если у вас на самом деле нет машины с 50 ядрами ЦП, использование 50 потоков / процессов не повлияет на производительность, а фактически замедлит работу.

Во-вторых, у меня такое ощущение, что если бы вы использовали способ SQLAlchemy , вставляющий несколько значений одновременно , это было бы намного быстрее, чем создание объектов ORM и сохранение их по одному.

0 голосов
/ 07 июля 2010

Рискну сказать, что время, потраченное на скрипт Python, находится в части загрузки каждой записи. Чтобы определить это, вы можете написать в CSV или отменить результаты вместо загрузки новых записей. Это определит, где находится узкое место; по крайней мере, с точки зрения поиска-вставки. Если, как я подозреваю, это действительно то, где вы можете воспользоваться преимуществами массового импорта, которые есть в большинстве DBS. Нет никаких оснований и даже некоторых аргументов против вставки записи за записью в такого рода обстоятельствах.

При массовом импорте, как правило, выполняется некоторая оптимизация интереса, такая как выполнение одной транзакции без фиксации для каждой записи (даже простое выполнение этого может привести к заметному сокращению времени выполнения); когда это возможно, я рекомендую массовую вставку для большого количества записей. Вы все еще могли бы использовать подход «производитель / потребитель», но потребителю вместо этого сохранить значения в памяти или в файле и затем вызвать оператор массового импорта, специфичный для используемой вами БД. Это может быть путь, если вам нужно выполнить обработку для каждой записи в файле CSV. Если это так, я бы также подумал, сколько из этого может быть кэшировано и разделено между записями.

также возможно, что узкое место использует SQLAlchemy. Не то чтобы я знал о каких-либо внутренних проблемах, но, учитывая то, что вы делаете, это может потребовать намного больше обработки, чем необходимо - о чем свидетельствует 8-кратное различие во времени выполнения.

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

...