2012-04-02 117 views
1

我需要插入一個MySQL的InnoDB表從遠程客戶端的數據行的許多數以萬計。客戶端(Excel的VBA超過經由ADO MySQL的ODBC連接器)可以生成CSV和執行LOAD DATA LOCAL INFILE,或者可以準備一個巨大INSERT INTO ... VALUES (...), (...), ...語句和執行。前者需要一些rather ugly hacks來克服Excel's inability to output Unicode CSV natively(它只在系統區域設置的默認代碼頁中寫入CSV,在許多情況下它是單字節字符集,因此非常有限);但MySQL documentation表明它可能比後一種方法快20倍(爲什麼?),它也「感覺」,好像由於SQL命令極長而可能不太穩定。速度INSERT VS LOAD DATA LOCAL INFILE的

我還沒有能夠標杆兩種方法,但我會聽到的可能性能/穩定性問題的想法很感興趣。

回答

1

我想也許一個混合解決方案將工作做好在這裏...是... ...

首先創建性能

PREPARE stmt1 FROM 'INSERT INTO table (column1, column2, ...) VALUES (?, ?, ...)'; 

一份聲明中觀察到的?標記是實際的語法 - 無論您打算最終如何使用從CSV文件解析的值,都可以使用問號。

編寫一個打開.CSV文件並進入循環的過程或函數,該循環一次讀取一行內容(一次一個記錄),將解析列的值存儲在單獨的變量中。

然後,在這個循環中,只是讀出記錄到本地變量後,您可以設置在準備好的聲明中的值在局部變量的當前記錄,如...

SET @a = 3; 
SET @b = 4; 

應該有SET語句的數量與CSV文件中的列數相同。如果沒有,你錯過了一些東西。順序是非常重要的,因爲您必須根據?的位置設置值。在準備好的聲明中標記。這意味着您將必須確保SET語句與INSERT語句中的列匹配。

所有參數的設置對於準備語句之後,然後執行它。

EXECUTE stmt1 USING @a, @b; 

然後這是循環的結束。剛退出循環(到達CSV文件結束後),您必須釋放準備的語句,如...

DEALLOCATE PREPARE stmt1; 

重要的事情要記住的是...

製作確保在進入循環讀取記錄之前準備好INSERT語句,並確保在退出循環後DEALLOCATE語句。

預處理語句允許數據庫預編譯和優化語句一次,然後多次不斷變化的參數值執行。這應該會提高性能。

我不確定MySQL,但有些數據庫還允許您在準備好的語句在網絡中實際執行之前指定要緩存的行數 - 如果MySQL有這種可能性,這樣做可以讓您告訴數據庫儘管您正在從CSV中讀取的每一行調用execute語句,但數據庫應該將語句批量添加到指定的行數,然後才能通過網絡執行。通過這種方式,性能會大大提高,因爲數據庫可能會批量增加5或10個INSERTS,並在網絡上只使用一次往返而不是每行執行一次。

希望這有助於並有意義。祝你好運!

Rodney

+0

謝謝羅德尼 - 有趣的方法。事實證明,其他一些(令人沮喪的)問題阻止我們每個數據庫會話發送多個命令;所以我們必須建立TCP,對MySQL進行身份驗證,發送單個查詢,拆除會話並重新開始。這不僅會增加額外開銷,導致單獨發送10K個記錄,當然,準備好的語句也不會在各個會話中持續存在。因此,我採用醜陋的黑客方式爲'LOAD DATA'輸出UTF-8 CSV。 – eggyal 2012-04-04 00:18:11