2011-09-27 107 views
2

一個接我們正在運行一個Web應用程序使用的查詢如下:優化MySQL查詢與若干個連接

SELECT 
     p.id, r.id AS report_id, tr.result_id, 
     r.report_date, r.department, r.reportStatus, rs.specimen, 
     tr.name, tr.value, tr.flag, tr.unit, tr.reference_range 
FROM patients AS p 
INNER JOIN 
    patients_reports AS pr ON pr.patient_id = p.id 
INNER JOIN 
    reports AS r ON pr.report_id = r.id 
INNER JOIN 
    results AS rs ON r.id = rs.report_id 
INNER JOIN 
    test_results AS tr ON rs.id = tr.result_id 
WHERE pr.patient_id = 17548 
ORDER BY rs.specimen, tr.name, r.report_date; 

解釋計劃是這樣的:

+----+-------------+-------+--------+---------------+-----------+---------+-------------------+--------+----------------------------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref    | rows | Extra          | 
+----+-------------+-------+--------+---------------+-----------+---------+-------------------+--------+----------------------------------------------+ 
| 1 | SIMPLE  | p  | const | PRIMARY  | PRIMARY | 4  | const    |  1 | Using index; Using temporary; Using filesort | 
| 1 | SIMPLE  | rs | ALL | PRIMARY  | NULL  | NULL | NULL    | 152817 |            | 
| 1 | SIMPLE  | r  | eq_ref | PRIMARY  | PRIMARY | 4  | demo.rs.report_id |  1 |            | 
| 1 | SIMPLE  | pr | eq_ref | PRIMARY  | PRIMARY | 8  | const,demo.r.id |  1 | Using where; Using index      | 
| 1 | SIMPLE  | tr | ref | result_id  | result_id | 5  | demo.rs.id  |  1 | Using where         | 
+----+-------------+-------+--------+---------------+-----------+---------+-------------------+--------+----------------------------------------------+ 

查詢返回27371行。目前test_results中有152730行。這只是少量的演示數據。

我試圖讓查詢更有效率,但我很難讓它更快地執行。我已經看過關於文檔和關於stackoverflow的問題的各種文章,但還沒有能夠解決這個問題。

我試圖消除的加入如下之一:

SELECT 
     pr.patient_id, r.id AS report_id, tr.result_id, 
     r.report_date, r.department, r.reportStatus, rs.specimen, 
     tr.name, tr.value, tr.flag, tr.unit, tr.reference_range 
FROM patients_reports AS pr 
INNER JOIN 
    reports AS r ON pr.report_id = r.id 
INNER JOIN 
    results AS rs ON r.id = rs.report_id 
INNER JOIN 
    test_results AS tr ON rs.id = tr.result_id 
WHERE pr.patient_id = 17548 
ORDER BY rs.specimen, tr.name, r.report_date; 

的查詢計劃是那麼如下:

+----+-------------+-------+--------+---------------+-----------+---------+-------------------+--------+---------------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref    | rows | Extra       | 
+----+-------------+-------+--------+---------------+-----------+---------+-------------------+--------+---------------------------------+ 
| 1 | SIMPLE  | rs | ALL | PRIMARY  | NULL  | NULL | NULL    | 152817 | Using temporary; Using filesort | 
| 1 | SIMPLE  | r  | eq_ref | PRIMARY  | PRIMARY | 4  | demo.rs.report_id |  1 |         | 
| 1 | SIMPLE  | pr | eq_ref | PRIMARY  | PRIMARY | 8  | const,demo.r.id |  1 | Using where; Using index  | 
| 1 | SIMPLE  | tr | ref | result_id  | result_id | 5  | demo.rs.id  |  1 | Using where      | 
+----+-------------+-------+--------+---------------+-----------+---------+-------------------+--------+---------------------------------+ 

所以沒有太大的不同。

我已經嘗試重新排列查詢和使用STRAIGHT_JOIN等事物,但我沒有得到任何地方。

我很感激一些關於如何優化查詢的建議。謝謝。

編輯:啊!我並沒有對results.report_id指數,但它似乎並沒有幫助:

+----+-------------+-------+--------+-------------------+-----------+---------+-------------------+--------+---------------------------------+ 
| id | select_type | table | type | possible_keys  | key  | key_len | ref    | rows | Extra       | 
+----+-------------+-------+--------+-------------------+-----------+---------+-------------------+--------+---------------------------------+ 
| 1 | SIMPLE  | rs | ALL | PRIMARY,report_id | NULL  | NULL | NULL    | 152817 | Using temporary; Using filesort | 
| 1 | SIMPLE  | r  | eq_ref | PRIMARY   | PRIMARY | 4  | demo.rs.report_id |  1 |         | 
| 1 | SIMPLE  | pr | eq_ref | PRIMARY   | PRIMARY | 8  | const,demo.r.id |  1 | Using where; Using index  | 
| 1 | SIMPLE  | tr | ref | result_id   | result_id | 5  | demo.rs.id  |  1 | Using where      | 
+----+-------------+-------+--------+-------------------+-----------+---------+-------------------+--------+---------------------------------+ 

EDIT2:

patients_reports看起來是這樣的:

+------------+---------+------+-----+---------+-------+ 
| Field  | Type | Null | Key | Default | Extra | 
+------------+---------+------+-----+---------+-------+ 
| patient_id | int(11) | NO | PRI | 0  |  | 
| report_id | int(11) | NO | PRI | 0  |  | 
+------------+---------+------+-----+---------+-------+ 

EDIT3:

在添加results.report_id索引並按照@DRapp建議再次嘗試STRAIGHT_JOIN之後:

SELECT STRAIGHT_JOIN 
     r.id AS report_id, tr.result_id, 
     r.report_date, r.department, r.reportStatus, rs.specimen, 
     tr.name, tr.value, tr.flag, tr.unit, tr.reference_range 
FROM patients_reports AS pr 
INNER JOIN 
    reports AS r ON pr.report_id = r.id 
INNER JOIN 
    results AS rs ON r.id = rs.report_id 
INNER JOIN 
    test_results AS tr ON rs.id = tr.result_id 
WHERE pr.patient_id = 17548 
ORDER BY rs.specimen, tr.name, r.report_date; 

的計劃是這樣的:

+----+-------------+-------+--------+-------------------+-----------+---------+-------------------+------+----------------------------------------------+ 
| id | select_type | table | type | possible_keys  | key  | key_len | ref    | rows | Extra          | 
+----+-------------+-------+--------+-------------------+-----------+---------+-------------------+------+----------------------------------------------+ 
| 1 | SIMPLE  | pr | ref | PRIMARY   | PRIMARY | 4  | const    | 3646 | Using index; Using temporary; Using filesort | 
| 1 | SIMPLE  | r  | eq_ref | PRIMARY   | PRIMARY | 4  | demo.pr.report_id | 1 |            | 
| 1 | SIMPLE  | rs | ref | PRIMARY,report_id | report_id | 5  | demo.r.id   | 764 | Using where         | 
| 1 | SIMPLE  | tr | ref | result_id   | result_id | 5  | demo.rs.id  | 1 | Using where         | 
+----+-------------+-------+--------+-------------------+-----------+---------+-------------------+------+----------------------------------------------+ 

所以我認爲,看起來好多了,但我不知道到底如何告訴。此外,查詢似乎仍然與以前大致相同。

+1

如果您刪除,會發生什麼ORDER BY子句,或至少'specimen'排序?你似乎把所有的行都從'results'中拉出來,而你並不需要它們。 –

+0

這聽起來像是你要求27371/152730 =〜18%的數據。我錯過了什麼嗎?全面掃描非常有可能是拉動18%任何數據集的最快方法。 (還有一個我試圖給出的答案,這就是「嘗試PostgreSQL」) – derobert

+0

@Larry Lustig:刪除ORDER BY似乎沒有什麼區別。 – Wodin

回答

0

是results.report_id索引?它沒有找到一個密鑰,並做一個表掃描它看起來像。我假設results.id實際上是主鍵。

另外,如果REPORT_ID是主鍵,這是INNODB,應該對指數進行聚類,因此完全不知道這是爲什麼不尖叫快,如果它是配置這種方式。

+0

謝謝,我已經添加了一些索引來達到這一點,但錯過了results.report_id。雖然這似乎沒有幫助。 – Wodin

+0

我們談論的速度有多快?什麼樣的硬件?你如何基於速度執行查詢?感冒還是緩存? – StrangeWill

+0

report_id不是主鍵。有一個自動增量ID是主鍵,因爲每個報表有多個結果。這是InnoDB。該盒子是Apple Xserve。 「基準測試」只是運行來自「mysql」命令的查詢,如果我連續運行查詢幾次,我會得到約3.5秒。 – Wodin

1

我會使用STRAIGHT_JOIN並與您的第二個查詢有第一個和第二個patient_reports表加入患者表爲他們的名字信息。此外,如果我沒有看到它,PATIENT_ID列中的patients_reports表中是否有索引,或者是它本身,或者是複合索引鍵的第一個元素?

此外,確保結果對REPORT_ID的指數,同樣與TEST_RESULTS(上Result_ID指數)

+0

該查詢未使用患者姓名。名稱字段來自test_results表。所以我的第二個查詢完全刪除了患者表,因爲它看起來沒有必要。我已更新帖子以顯示如何定義patients_reports表。我現在已經爲results.report_id添加了一個索引。 test_results在result_id上已經有一個索引。添加results.report_id索引後,我試過了STRAIGHT_JOIN,而且似乎已經完成了。 – Wodin

+0

我*認爲*現在看起來更好,但查詢似乎沒有更快。 – Wodin