2010-10-15 85 views
12

我正在開發一個使用MySQLdb訪問MySQL數據庫的Python程序。在某些情況下,我必須在許多行上運行INSERT或REPLACE命令。我目前這樣做:爲什麼executemany在Python MySQLdb中變慢?

db.execute("REPLACE INTO " + table + " (" + ",".join(cols) + ") VALUES" + 
    ",".join(["(" + ",".join(["%s"] * len(cols)) + ")"] * len(data)), 
    [row[col] for row in data for col in cols]) 

它工作正常,但它有點尷尬。我想知道如果我能夠更容易閱讀,並且我發現了有關executemany命令。我改變了我的代碼,看起來像這樣:

db.executemany("REPLACE INTO " + table + " (" + ",".join(cols) + ") " + 
    "VALUES(" + ",".join(["%s"] * len(cols)) + ")", 
    [tuple(row[col] for col in cols) for row in data]) 

它仍然工作,但它跑了很多慢。在我的測試中,對於相對較小的數據集(約100-200行),它運行速度慢了6倍。對於大數據集(大約13,000行,我希望處理的數據量最大),它的運行速度慢了大約50倍。它爲什麼這樣做?

我真的想簡化我的代碼,但我不希望性能大幅下降。有誰知道有什麼方法可以使其更快?

我使用Python 2.7和MySQLdb 1.2.3。我試着修改setinputsizes函數,但這似乎沒有做任何事情。我查看了MySQLdb源代碼,看起來它不應該做任何事情。

+0

你插入/替換多少行?你的第二條語句在把它提供給mysql之前會在內存中創建一個巨大的列表 – nosklo 2010-10-15 19:59:08

+1

我正在替換多達13,000行。我認爲創建清單並不是瓶頸。如果我創建列表但不將它傳遞給db光標,它幾乎不需要任何時間。 – 2010-10-15 20:14:36

+0

(不會回答這個問題,但是......)'INSERT ... ON DUPLICATE KEY UPDATE ...'幾乎總是比'REPLACE ...'更好。 – 2017-05-26 05:24:45

回答

19

嘗試在查詢中降低「值」一詞 - 這似乎是MySQL-python 1.2.3中的錯誤/迴歸。

MySQL-python的executemany()實現與正則表達式的VALUES子句相匹配,然後僅克隆每行數據的值列表,因此您最終將執行與第一種方法完全相同的查詢。

不幸的是,正則表達式失去了它不區分大小寫的標誌在該版本中(隨後固定在軀幹r622但從來沒有回遷到1.2分支),因此它會降低到遍歷數據,併發射了每行的查詢。

+0

我試過了,它的工作原理!對於「值」小寫字母,executemany的執行速度與執行速度一樣快,或者有時候會更快一些。 – 2010-10-17 00:59:16

+1

請注意,1.2.3正則表達式不能在ON DUPLICATE KEY UPDATE查詢中使用參數(正則表達式只匹配第一個參數),所以lower-casing值會導致混淆(因爲它們與execute()一起使用)「not在字符串格式化期間轉換的所有參數「錯誤。爲避免使用VALUES()格式,而不是查詢的ON DUPLICATE KEY部分中的參數。 – 2011-04-12 22:09:00

+0

它已在[1.2.4](https://github.com/farcepest/MySQLdb1/blob/MySQLdb-1.2.4/MySQLdb/cursors.py#L43)中修復。 – saaj 2015-09-27 13:02:06

1

您的第一個示例是生成併發送到數據庫的單個(大)語句。

第二個例子是一個簡單得多的語句,插入/替換單個行但多次執行。每條命令都單獨發送到數據庫,因此您必須支付從客戶端到服務器的週轉時間,並返回插入的每一行。我認爲這些命令之間引入的額外延遲是導致第二個示例性能下降的主要原因。

+0

這就是我所懷疑的。我想也許executemany函數足夠複雜,可以在一個查詢中發送所有命令,但它看起來並不像它。 – 2010-10-15 20:15:52

1

強烈建議不要使用executeManypyodbc以及ceodbc都很慢並且包含很多錯誤。

請考慮使用execute並使用簡單的字符串格式手動構造SQL查詢。

transaction = "TRANSACTION BEGIN {0} COMMIT TRANSACTION 

bulkRequest = "" 
for i in range(0, 100) 
    bulkRequest = bulkRequest + "INSERT INTO ...... {0} {1} {2}" 

ceodbc.execute(transaction.format(bulkRequest)) 

當前實現非常簡單快速可靠。

相關問題