2009-10-22 54 views
0

這個查詢真的很慢。我注意到9和10秒之間......任何方式來加快此查詢?

SELECT DISTINCT a.* 
FROM addresses a 
LEFT JOIN contacts c 
ON c.id = a.contact_id 
LEFT JOIN organizations o 
ON o.id = a.organization_id 
ORDER BY c.last_name, c.first_name, o.name 
LIMIT 0, 24 

如果註釋掉ORDER BY子句的查詢運行得更快 - 約5毫秒。但我需要ORDER BY來支持分頁搜索結果。用戶需要通過聯繫和組織對地址進行排序。


表結構

addresses 
--------- 
id int NOT NULL 
contact_id int  # could be NULL 
organization_id int # could be NULL 

contacts 
-------- 
id int NOT NULL 
first_name varchar(255) 
last_name varchar(255) 

organizations 
------------- 
id int NOT NULL 
name varchar(255) 

他們都是InnoDB表。

我對接點表這些指標:

KEY `idx_contacts_first_name` (`first_name`), 
    KEY `idx_contacts_last_name` (`last_name`), 
    KEY `idx_contacts_first_name_last_name` (`first_name`,`last_name`) 

而且在組織表:

KEY `idx_organization_name` (`name`) 

數據量

Addresses:  22,271 
Contacts:  17,906 
Organizations: 8,246 

說明OUTPUT

mysql> DESCRIBE 
    -> SELECT DISTINCT a.* 
    -> FROM addresses a 
    -> LEFT JOIN contacts c 
    -> ON c.id = a.contact_id 
    -> LEFT JOIN organizations o 
    -> ON o.id = a.organization_id 
    -> ORDER BY c.last_name, c.first_name, o.name 
    -> LIMIT 0, 24; 
+----+-------------+-------+--------+---------------+---------+---------+--------------------------------------------+-------+---------------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref          | rows | Extra       | 
+----+-------------+-------+--------+---------------+---------+---------+--------------------------------------------+-------+---------------------------------+ 
| 1 | SIMPLE  | a  | ALL | NULL   | NULL | NULL | NULL          | 22387 | Using temporary; Using filesort | 
| 1 | SIMPLE  | c  | eq_ref | PRIMARY  | PRIMARY | 4  | contactdb_v2_development.a.contact_id  |  1 | Distinct      | 
| 1 | SIMPLE  | o  | eq_ref | PRIMARY  | PRIMARY | 4  | contactdb_v2_development.a.organization_id |  1 | Distinct      | 
+----+-------------+-------+--------+---------------+---------+---------+--------------------------------------------+-------+---------------------------------+ 
3 rows in set (0.00 sec) 

回答

1

如果你沒有太多資源受限的服務器端,這件事情是不會擴展得太遠,你沒有大量的數據所以你可以簡單地在那個級別上做你的訂購和分頁。

+0

你是什麼意思的「做你的訂單和分頁在該級別。」?您是否建議在從數據庫中檢索數據後,在應用程序代碼中進行排序? – sleske 2009-10-22 23:42:36

+0

是的。對於一個大約20,000行的數據集,這個數據集可能不會發生太大的變化,所以對數據進行抽取,分類和緩存是有意義的。 – 2009-10-22 23:56:32

+0

是的,我想我會用這樣的東西。將所有記錄提供給分頁可能看起來很奇怪,但如果不這樣做會導致令人痛苦的UI複雜性。 – Ethan 2009-10-24 00:55:21

1

嘗試增加這個指數:

idx_contacts_last_name_first_namelast_namefirst_name

BTW:你可以刪除idx_contacts_first_name,因爲它是重複的,如果你加入這個指數可以刪除idx_contacts_last_name。

1

試着改變你的SQL的東西像下面這樣:

SELECT a.column1, a.column2, ... 
FROM addresses a 
LEFT JOIN contacts c 
ON c.id = a.contact_id 
LEFT JOIN organizations o 
ON o.id = a.organization_id 
GROUP BY a.column1, a.column2, ... 
ORDER BY c.last_name, c.first_name, o.name 
LIMIT 0, 24 

我發現GROUP BY是普遍比DISTINCT快很多,雖然我不知道爲什麼會。

2

我試過你的例子,數據量相近,在筆記本電腦(Pentium M 1,7 GHz)上,查詢時間不到一秒鐘(第一次運行,後來運行更少)。

你是否偶然忘記了id列上的PK?你沒有提到它,所以只是問......如果你忘記了這一點,性能顯然會非常糟糕 - 更不用說每個DBA都會在沒有PK的桌子上畏縮。

否則,試試這個:

DESCRIBE <your query> 

這會給你的MySQL的查詢計劃。發佈(編輯你的問題),並且應該更清楚需要這麼長時間。

在進一步的思考:

查詢總是有問題的表現,因爲你問的數據庫中讀取和排序所有地址並顯示出來。 ORDER BY意味着它在返回任何東西之前必須閱讀所有內容,所以它總是很慢。什麼是這樣的整個數據庫?用戶是否會翻閱幾千條記錄?

考慮例如允許搜索查詢。使用WHERE條件時,查詢將快得多。

+0

檢查PKs。它們被設定。我粘貼了DESCRIBE輸出。應用程序確實提供了一個搜索表單(更確切地說,是一個過濾表單)。 all-Addresses列表是未輸入過濾參數時的默認狀態。將所有地址以外的東西設置爲默認狀態會很尷尬,雖然並非不可能。我正在考慮這個選項,儘管我很困惑它會在你的機器上運行得更快。如果我能顯示「所有地址」,這對用戶來說是最不容易混淆的。 – Ethan 2009-10-22 23:47:34

0

讓我們來看看。

  • 地址:22271
  • 聯繫人:17,906
  • 組織:8246個

地址的LEFT JOIN接觸C給出約20000 * 20000〜4億比較,爲約20,000結果

LEFT JOIN組織給出約10,000 * 20,000〜200萬的比較約20,000結果

,我們主要在接觸行上排序,然後丟棄其中的24個。似乎地址的獨特性是微不足道的。

由於我們主要通過接觸排序,怎麼樣,我們做的聯繫人子選擇,保持 有點多(比如說,通過約4倍),比我們需要:

SELECT * FROM contacts ORDER BY last_name, first_name LIMIT 100 

然後加入那些他們的地址保持頂部百元左右

 SELECT a.* 
     FROM (SELECT * FROM contacts ORDER BY last_name, first_name LIMIT 0, 100) AS c 
    LEFT JOIN addresses a 
     ON c.id = a.contact_id 
     LIMIT 0, 100 

再加入這些到組織

SELECT * 
    FROM (
     SELECT * 
      FROM (SELECT * FROM contacts ORDER BY last_name, first_name LIMIT 0, 100) AS c 
    LEFT JOIN addresses a 
      ON c.id = a.contact_id 
     LIMIT 0, 100 
     ) AS ca LEFT JOIN organizations o 
     ON o.id = ca.organization_id 
ORDER BY ca.last_name, ca.first_name, o.name 
    LIMIT 0, 24 

我敢肯定,語法是搞砸了,但我同樣確信削減在每個階段設定的結果的原則點指示性的方式。我可能也做了一些折衷,這樣的結果非常接近10秒的答案,但得到更快。