Как избежать многократных циклов для запросов с MySQLdb с Python? - PullRequest
0 голосов
/ 11 февраля 2012

Я читаю в некоторых сырых файлах и вставляю их в БД.Это будет включать МИЛЛИОНЫ записей, и для каждой записи у меня есть несколько вставок (много таблиц).Когда я проводил локальное тестирование, все шло быстро, но для всего набора данных мне нужно было работать с удаленной базой данных.Это мучительно медленно, что, как я полагаю, происходит из-за всех попыток удаления / вставки по сети.

Я использую модуль MySQLdb (python), и на данный момент у меня есть такие вещи, какследующее:

# setup connection
con = mdb.connect('remote.host', 'database_user', '123456789', 'database_name');

... read files, loop through records, etc...

# clear out data related to current record
cur.execute("DELETE FROM articles WHERE article_id = %s", article.id)
cur.execute("DELETE FROM authors WHERE article_id = %s", article.id)
cur.execute("DELETE FROM addresses WHERE article_id = %s", article.id)
cur.execute("DELETE FROM citation_references WHERE article_id = %s", article.id)
cur.execute("DELETE FROM citation_patents WHERE article_id = %s", article.id)

# insert the article
cur.execute("INSERT INTO articles (article_id, doctype, keywords, language, title) VALUES (%s, %s, %s, %s, %s, %s)" , (article.id, article.doctype, ';'.join(article.keywords), article.language, article.title))

# insert all the authors
for au in article.authors:
    cur.execute("INSERT INTO isi_authors (article_id, name_first, name_last, email) VALUES (%s, %s, %s, %s)", (article.id, au.first_name, au.last_name, au.email))

... other loops like the authors to insert 10-20 citations per article, multiple addresses, etc ...

Из того, что я могу сказать, MySQLdb не позволяет мне отправлять несколько запросов одновременно.Должен быть способ избежать сетевых задержек.Есть идеи?

Ответы [ 3 ]

4 голосов
/ 12 июня 2012

По крайней мере, MySQLdb 1.2.3, кажется, разрешает несколько запросов из коробки, вам просто нужно вызвать cursor.nextset() для циклического перебора возвращаемых наборов результатов.

db = conn.cursor()
db.execute('SELECT 1; SELECT 2;')

more = True
while more:
    print db.fetchall()
    more = db.nextset()

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

MYSQL_OPTION_MULTI_STATEMENTS_ON = 0
MYSQL_OPTION_MULTI_STATEMENTS_OFF = 1

conn.set_server_option(MYSQL_OPTION_MULTI_STATEMENTS_ON)
# Multiple statement execution here...
conn.set_server_option(MYSQL_OPTION_MULTI_STATEMENTS_OFF)

Если при выполнении одного из запросов произошла ошибка, mysql не выполнит никаких запросов после этой точки. Вызов db.execute() сгенерирует исключение, если оно возникло из первого запроса, в противном случае соответствующий db.nextset() сделает это, так что вы можете извлечь наборы результатов из успешных запросов до получения исключения.

1 голос
/ 11 февраля 2012

Синтаксис mySQL INSERT позволяет это делать.Сравните 1) и 2)

1. INSERT INTO tbl_name (a,b,c) VALUES(1,2,3);
2. INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);

Во втором случае вы вставляете три строки одновременно.

http://dev.mysql.com/doc/refman/5.5/en/insert.html

Надеюсь, это даст вам идеи.

PS: это не зависит от языка

1 голос
/ 11 февраля 2012

Используйте executemany.Вот пример из руководства :

c.executemany(
      """INSERT INTO breakfast (name, spam, eggs, sausage, price)
      VALUES (%s, %s, %s, %s, %s)""",
      [
      ("Spam and Sausage Lover's Plate", 5, 1, 8, 7.95 ),
      ("Not So Much Spam Plate", 3, 2, 0, 3.95 ),
      ("Don't Wany ANY SPAM! Plate", 0, 4, 3, 5.95 )
      ] )

В вашем случае это будет выглядеть примерно так:

sql = "INSERT INTO isi_authors (article_id, name_first, name_last, email) VALUES (%s, %s, %s, %s)"
params = [(article.id, au.first_name, au.last_name, au.email) for au in article.authors]
cur.executemany(sql, params)

Из документации executemany:

Этот метод повышает производительность для многострочных INSERT и REPLACE.В противном случае это эквивалентно циклическому изменению аргументов с помощью execute ().

...