2012-08-31 62 views
2

我正在連接到一個我無法管理的數據庫,並且我寫了一個查詢,它在兩個表之間進行了左連接 - 一個小一個數量級,大一個數量級。在某些時候,數據庫返回了這個錯誤:做「small_table LEFT JOIN large_table」是不好的做法嗎?

Incorrect key file for table '/tmp/#sql_some_table.MYI'; try to repair it

我聯繫了管理員,我被告知,因爲我做的是左邊的是我得到這個錯誤加入錯誤,我應該從來沒有離開過加入一個小表到一個大表,我應該反轉連接順序。他們給出的理由是,當我按照我的方式完成時,MySQL將嘗試創建一個太大的臨時表,並且查詢將失敗。他們的解決方案在其他地方失敗了,但這並不重要。

我找到了自己的解釋奇怪,所以我跑我的查詢說明:

  id = '1' 
    select_type = 'SIMPLE' 
     table = 'small_table' 
     type = 'ALL' 
possible_keys = NULL 
      key = NULL 
     key_len = NULL 
      ref = NULL 
     rows = '23' 
     Extra = 'Using temporary; Using filesort' 

      id = '1' 
    select_type = 'SIMPLE' 
     table = 'large_table' 
     type = 'ref' 
possible_keys = 'ID,More' 
      key = 'ID' 
     key_len = '4' 
      ref = 'their_db.small_table.ID' 
     rows = '41983' 
     Extra = NULL 

(在41983行中第二個表是不是對我來說很有趣,我只是需要最新的記錄,這是爲什麼我的查詢在最後有order by large_table.ValueDateTime desc limit 1。)

我非常小心地按管理員自己告訴我的列進行選擇,應該保存唯一值(因此我假定已編制索引),但似乎他們沒有編制索引那些專欄。

我的問題是 - 按照我做過的方式進行連接('small_table LEFT JOIN large_table')一般的錯誤練習,還是可以通過適當的索引成功執行這樣的查詢?

編輯: 這裏的查詢是什麼樣子(這是不實際的查詢,但類似):

select large_table.ValueDateTime as LastDate, 
     small_table.DeviceIMEI as IMEI, 
     small_table.Other_Columns as My_Names, 
     large_table.Pwr as Voltage, 
     large_table.Temp as Temperature 
from small_table left join large_table on small_table.ID = large_table.ID 
where DeviceIMEI = 500 
order by ValueDateTime desc 
limit 1; 

基本上我做的是試圖獲得最新數據,設備,因爲電壓和溫度隨時間而變化。 DeviceIMEI,ID和ValueDateTime應該是唯一的,但沒有索引(就像我之前所說的,我不管理數據庫,我只有讀權限)。

編輯2:

請專注於回答我的實際問題,而不是試圖重寫我的原始查詢。

+1

另一個例子,爲什麼MySQL幾乎是一個很好的RDMS ...我不能相信管理員可以說一些soooo愚蠢:「反向連接命令」。好,當然。 –

+0

尼古拉,你有多少行沒有這個'限制1'? –

+0

@KubaWyrostek如果我刪除了限制1,結果會得到200652行。 –

回答

1

左邊的連接是紅色的鯡魚。

然而,它是臨時表空間用盡的實際問題。但你的加入順序沒有任何區別。唯一重要的是MySQL必須使用多少行。

這使我到了極限命令:

這裏的問題是:

它爲了得到單列你問,MySQL有排序的整個記錄​​集,然後抓住前一個。爲了對它進行排序,它必須將其存儲在內存中或磁盤上。這就是你的空間不足的地方。您請求的每一個列都存儲在磁盤上,整個表中,然後排序。

這很慢,很慢,並且使用大量磁盤空間。

解決方案:

您希望MySQL能夠使用索引進行排序。但在你的查詢中它不能。它使用連接引用的索引,並且MySQL每個查詢只能使用一個索引。

你甚至在排序欄上有索引嗎?先嚐試一下。

另一個選擇是做一個單獨的查詢,在這裏你只選擇大表的ID,LIMIT 1。然後臨時表就會更小,因爲它所有的ID都是沒有其他列的ID。

一旦你知道了ID,然後直接從表中檢索你需要的所有列。您可以使用子查詢一次性完成此操作。如果你發佈你的查詢,我可以重寫它來顯示你,但它基本上是ID = (SELECT ID FROM ..... LIMIT 1)

+0

正如我所說,我不管理數據庫,我不能添加索引。我只有讀取權限。此外,加入的順序也有所不同。查詢不會導致錯誤,但問題是,如果設備在large_table中沒有記錄,那麼沒有什麼可以加入small_table中的數據,事實上,它包含這樣一個設備的記錄。管理員告訴我在兩個單獨的查詢中這樣做,但這樣做會使得不必要地使用數據庫的應用程序複雜化。另外,我更新了我的問題以添加查詢。 –

+0

它的工作原理並不是因爲左連接有問題,而是因爲您正在對右表中的列進行排序。但是如果右表是空白的(缺失)會發生什麼?它應該如何排序?會發生什麼情況是,它無法查看大表並直接找到最近的日期 - 可能不是正確的行,因爲正確的行在大表中可能沒有記錄,並且只能在小表。所以它必須實際計算一切,然後排序。 – Ariel

+0

那麼告訴我,你對大表中的空白行有什麼期望?它應該如何排序?爲什麼你需要左連接?你永遠不會真的從小表中獲取記錄,這是在大表上缺少記錄的原因,因爲你正在按照大表上的值進行排序並只獲取其中的一個。缺失的行將永遠不會顯示。 – Ariel

相關問題