2013-10-09 108 views
9

我試圖將查詢結果存儲在臨時表中作進一步處理。爲什麼MySQL'insert into ... select ...'比單獨選擇慢得多?

create temporary table tmpTest 
(
    a FLOAT, 
    b FLOAT, 
    c FLOAT 
) 
engine = memory; 

insert into tmpTest 
(
    select a,b,c from someTable 
    where ... 
); 

但由於某些原因插入需要長達一分鐘,而再選擇單獨只需要幾秒鐘。爲什麼要將數據寫入臨時表而不是將其打印到SQL管理工具的輸出需要更長的時間?

UPDATE 我的設置: 的MySQL 7.3.2集羣與 8的Debian Linux的NDB數據節點 1 SQL節點(Windows Server 2012中)

我跑了選擇上的表是一個NDB表。

我試圖找出來,如果使用時的執行計劃會有所不同「插入..」,但他們看起來是一樣的: (抱歉格式,計算器沒有表)

 
id select_type  table  type possible_keys key  key_len ref     rows  Extra 
1 PRIMARY   <subquery3> ALL  \N    \N  \N  \N     \N   \N 
1 PRIMARY   foo   ref  PRIMARY   PRIMARY 3  <subquery3>.fooId 9747434  Using where 
2 SUBQUERY  someTable range PRIMARY   PRIMARY 3  \N     136933000 Using where with pushed condition; Using MRR; Using temporary; Using filesort 
3 MATERIALIZED tmpBar  ALL  \N    \N  \N  \N     1000  \N 

CREATE TABLE ... SELECT也很慢。 47秒對比5秒沒有表插入/創建。

+1

你應該更具體一些,並提供一些你正在寫的數據。另外'INSERT .. SELECT'語法是不同的。您的示例將導致錯誤。 –

+0

很好的問題。我真的不知道MySQL如何爲自己保留「內存」。根據系統的狀態,如果它通過操作系統api,它可能會*詢問*內存,然後將其作爲* ram *或* harddrive *空間。查看窗口的虛擬字節管理。 – Sebas

+0

您是否嘗試過使用CREATE TABLE ... SELECT類型的查詢進行比較? http://dev.mysql.com/doc/refman/5.0/en/create-table-select.html – xiankai

回答

0

原因與計算機讀寫方式以及臨時文件的工作方式有關。選擇正在讀取硬盤上索引文件中的數據,而插入正在使用臨時文件並正在寫入該文件。需要更多的RAM,這樣做更困難。 至於爲什麼它需要一分鐘,我不確定,但我認爲代碼可能有點不正確,因此會有所貢獻。

+0

我的臨時表的引擎明確地設置爲'memory'。這是不是意味着它應該留在RAM中?我的機器有32GB RAM,其中39 %使用,而我的臨時表只有1000行... – Ben

+0

@ben wehich os? – Sebas

+0

@Sebas看到我的更新 – Ben

1

我上面寫了一條評論,然後偶然發現了這個解決方法。

這將完成你想要做的事情。

SELECT * FROM aTable INTO OUTFILE '/tmp/atable.txt'; 
LOAD DATA INFILE '/tmp/atable.txt' INTO TABLE anotherTable; 

請注意,這樣做意味着以某種方式管理/ tmp表。如果您嘗試將數據選擇到已存在的OUTFILE中,則會出現錯誤。所以你需要生成唯一的臨時文件名。然後運行某種類型的cron作業來清理它們。

我猜INFILE和OUTFILE行爲有所不同。如果有人可以在這裏解釋一下mysql的行爲,我將不勝感激。

D

這是比使用INFILE/OUTFILE更好的方法。

SET TRANSACTION ISOLATION LEVEL READ COMMITTED; INSERT INTO aTable SELECT ... FROM ...

這裏有一個相關的職位爲:

How to improve INSERT INTO ... SELECT locking behavior

1

我遇到了同樣的問題,並與實際解決它的子查詢玩耍。 如果select具有大量的行,則需要很長時間才能插入數據。 實施例:

INSERT INTO b2b_customers (b2b_name, b2b_address, b2b_language) 
SELECT customer_name, customer_address, customer_language 
FROM customers 
WHERE customer_name LIKE "%john%" 
ORDER BY customer_created_date DESC 
LIMIT 1 

使用LIMIT組合插入數據是不是一個好的選擇。所以你可以使用2個單獨的查詢來獲取數據和插入,或者你可以使用子查詢。 例子:

INSERT INTO b2b_customers (b2b_name, b2b_address, b2b_language) 
SELECT * FROM (
SELECT customer_name, customer_address, customer_language 
FROM customers 
WHERE customer_name LIKE "%john%" 
ORDER BY customer_created_date DESC 
LIMIT 1 
) sub1 

這將是在不改變你的腳本一個快速的解決方案。

所以我不知道爲什麼它需要0.01秒運行子查詢和60秒來運行插入。我獲得了1000多個沒有限制的結果。在我的情況下,子查詢將性能從60秒提高到0.01秒。

+0

你不知道爲什麼它需要一個毫秒**閱讀**數據爲60秒**寫**超過1000條記錄?也許是因爲閱讀比寫作更快? – Mjh

+0

是的,這是真的,但即時使用限制1只寫1行。 – joevette

+0

'WHERE customer_name LIKE「%john%」' - 這是一個全表掃描。一旦掃描每一條記錄並將其縮小以檢索包含「john」的內容。這需要一段時間。然後你寫1條記錄。效率不高。 – Mjh

相關問題