簡體   English   中英

Mysql慢查詢:JOIN +多個WHERES + ORDER BY

[英]Mysql slow query: JOIN + multiple WHERES + ORDER BY

長時間潛伏,第一個問題!

我正在努力優化此查詢,該查詢選擇與所選過濾器匹配的價格最低的商品:

SELECT product_info.*, MIN(product_all.sale_price) as sale_price, product_all.buy_link
FROM product_info
NATURAL JOIN (SELECT * FROM product_all WHERE product_all.date = '2010-09-30') as product_all
WHERE (product_info.category = 2  
AND product_info.gender = 'W' )
GROUP BY product_all.prod_id
ORDER BY MIN(product_all.sale_price) ASC LIMIT 13

它解釋說:

| id | select_type | table        | type   | possible_keys                                             | key     | key_len | ref                 | rows   | Extra                           |  
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+  
|  1 | PRIMARY     | <derived2>   | ALL    | NULL                                                     | NULL    | NULL    | NULL                | 89801  | Using temporary; Using filesort | 
|  1 | PRIMARY     | product_info | eq_ref | PRIMARY,category_prod_id_retail_price,category_ret...     | PRIMARY | 4       | product_all.prod_id | 1      | Using where                     | 
|  2 | DERIVED     | product_all  | ref    | date_2                                                    | date_2  | 3       |                     | 144107 |                                 | 

我已經嘗試消除子查詢,直覺看起來更好但實際上需要更長時間:

SELECT product_info.*, MIN(product_all.sale_price) as sale_price, product_all.buy_link
FROM product_info
NATURAL JOIN product_all
WHERE (product_all.date = '2010-09-30'
AND product_info.category = 2 
AND product_info.gender = 'W' )
GROUP BY product_all.prod_id
ORDER BY MIN(product_all.sale_price) ASC LIMIT 13

它的解釋是:

| id | select_type | table        | type | possible_keys                                             | key                      | key_len | ref                               | rows | Extra                                        |  
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+  
|  1 | SIMPLE      | product_info | ref  | PRIMARY,category_prod_id_retail_price,category_ret...     | category_retail_price    | 5       | const                             | 269  | Using where; Using temporary; Using filesort | 
|  1 | SIMPLE      | product_all  | ref  | PRIMARY,prod_id,date_2                                    | prod_id                  | 4       | equipster_db.product_info.prod_id | 141  | Using where                                  | 

以下是表格:

CREATE TABLE `product_all` (
`prod_id` INT( 10 ) NOT NULL PRIMARY KEY ,
`ref_id` INT( 10) NOT NULL PRIMARY KEY ,
`date` DATE NOT NULL ,
`buy_link` BLOB NOT NULL ,
`sale_price` FLOAT NOT NULL
) ENGINE = MYISAM ;


CREATE TABLE `product_info` (
`prod_id` INT( 10 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`prod_name` VARCHAR( 200 ) NOT NULL,
`brand` VARCHAR( 50 ) NOT NULL,
`retail_price` FLOAT NOT NULL
`category` INT( 3 ) NOT NULL,
`gender` VARCHAR( 1 ) NOT NULL,
`type` VARCHAR( 10 ) NOT NULL
) ENGINE = MYISAM ;

我的問題:
哪個查詢結構似乎最優?
什么索引會優化這個查詢?
- 重要的是:在添加或刪除WHERE子句或使用不同的ORDER BY時,索引方法如何更改,例如按%off排序:

ORDER BY (1-(MIN(product_all.sale_price)/product_info.retail_price)) DESC  

編輯:兩個查詢的自然連接作用於prod_id(product_info中的一條記錄可以在product_all中有多個實例,這就是為什么需要對它們進行分組)

指數在mysql中產生了巨大的差異,一個錯誤的索引花了15分鍾的查詢用正確的指數花費了0.2秒,但它找到了正確的平衡,這通常是問題所在。 自然沒有一些樣本數據,如果下面的解決方案能夠在任何時候拯救你,那么很難說,但理論上應該這樣做。

為了回答你的問題,我會像這樣重新設計表格:

CREATE TABLE `product_all` ( 
`prod_id` INT( 10 ) NOT NULL, 
`ref_id` INT( 10) NOT NULL, 
`date` DATE NOT NULL , 
`buy_link` BLOB NOT NULL , 
`sale_price` FLOAT NOT NULL,
PRIMARY KEY (prod_id, ref_id) ,
INDEX date_Index (`date` ASC),
UNIQUE INDEX prod_price_Index (prod_id ASC, sale_price ASC)
) ENGINE = MYISAM ; 


CREATE TABLE `product_info` ( 
`prod_id` INT( 10 ) NOT NULL AUTO_INCREMENT, 
`prod_name` VARCHAR( 200 ) NOT NULL, 
`brand` VARCHAR( 50 ) NOT NULL, 
`retail_price` FLOAT NOT NULL, 
`category` INT( 3 ) NOT NULL, 
`gender` VARCHAR( 1 ) NOT NULL, 
`type` VARCHAR( 10 ) NOT NULL,
PRIMARY KEY (prod_id) ,
UNIQUE INDEX prod_id_name_Index (prod_id ASC, prod_name ASC),
INDEX category_Index (category ASC),
INDEX gender_Index (gender ASC)
) ENGINE = MYISAM ;

SELECT product_info.*, MIN(product_all.sale_price) as sale_price, product_all.buy_link         
FROM product_info         
NATURAL JOIN (SELECT * FROM product_all WHERE product_all.date = '2010-09-30') as product_all         
WHERE (product_info.category = 2           
AND product_info.gender = 'W' )         
GROUP BY product_all.prod_id         
ORDER BY MIN(product_all.sale_price) ASC LIMIT 13        

這里的性能提升是通過索引正在連接的主要字段並在where子句中顯示的。 就個人而言,我會選擇你的第一個查詢,因為當你想到它應該表現得更好。

據我所知,在第一個和第二個查詢中發生了什么:

  • 第一個查詢在進行自然連接之前被子查詢過濾,這意味着它只加入結果數據而不是整個表。
  • 第二個查詢是連接整個第二個表,然后將整批的結果行過濾回您想要的。

通常,您希望在主要連接字段以及where子句中使用最多的字段添加索引。 我還在一些您希望定期查詢的字段上添加了一些唯一索引,例如prod_id_name_Index。

如果你可以發布一些虛擬數據來提高你的性能,我可能會得到一個更快的解決方案,我可以進行基准測試。

是一種通過索引進入業績在mysql中的一篇文章,值得如果你想了解更多的讀取。

祝好運!

編輯:我第一次錯過了你的最后一個問題,答案是,如果你對主要加入字段進行索引然后改變到哪里只會略微影響整體性能,但是我在表格上添加的唯一索引應該考慮到大多數你想要查詢的東西。 要記住的主要事情是,如果您經常查詢或加入某個字段,那么它應該真正被編入索引,但是對您的訂單進行次要查詢和更改應該不必擔心重新調整索引策略。

性能方面,它從來都不是一件好事

select *

您應該使用單個列名稱。

select column1,column2 etc...

就個人而言,我是一個SQL極簡主義者,並避免任何類型的子查詢或連接不能索引列索引。

如果那不可能,我可能會單獨運行子查詢來收集我的密鑰,對它們進行客戶端站點排序,然后在(...)子句中構建where。

JohnVD提出了很多好處,但是如果你需要制作一個包含product_name的唯一密鑰,你應該真的看看它是否可以歸一化為它。

如果可能的話,索引varchar列是不惜一切代價的。 每個索引條目都與列的最大大小一樣大,即使它們通常只是其中的一小部分。 如果你使用像utf-8這樣的字符集,則大小為~maxlen + 3。

有了你的限制,似乎需要訂單。 但就像你在做一個小組時的FYI一樣,如果你打算使用整個結果集,那么就使用ORDER BY NULL。 通過解釋運行這兩個變體,看看為什么; order by null消除了隱含的filesort,你可以對客戶端進行排序。 (如果你正在使用匯總進行分組,這是不可能的)

你應該堅持第二個查詢。 在列上使用索引可以最大程度地減少受影響的行。 在這種情況下,它可能是日期。 如果過濾條件總是包含多個列,則應嘗試多列索引。 MySQL只會使用一個索引。

正如米奇所說,試圖找到自然會有較低記錄數的標准肯定會贏得表現。 如果Category + Gender非常常見,請將其作為BOTH列的索引。 此外,一旦找到最佳條件,您可以更改以下查詢以更好地匹配它。 “STRAIGHT_JOIN”告訴MySQL按你所說的順序執行,而不是嘗試更改用於查詢基礎和加入其他的主表...所以,我不知道哪個更准確的類別索引,性別或日期...如果日期將有更少的記錄基礎,那么我會將THAT作為FROM子句中的第一個表交換,並在心理上將日期的IT標准移動到WHERE子句的第一個位置(僅我個人)與視覺保持同步()。 我已經看到STRAIGHT_JOIN在許多情況下顯着提高了性能,否則這些情況似乎是簡單的查詢。

SELECT STRAIGHT_JOIN
      product_info.*, 
      MIN(product_all.sale_price) as sale_price, 
      product_all.buy_link 
   FROM 
      product_info,
      product_all 
   where 
          product_info.category = 2   
      AND product_info.gender = 'W'
      and product_info.prod_id = product_all.prod_id
      AND product_all.date = '2010-09-30'
   GROUP BY 
      product_info.prod_id 
   ORDER BY 
      MIN(product_all.sale_price) ASC 
   LIMIT 13 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM