[英]how can I make this query more efficient?
編輯:這是原始查詢的簡化版本(在475K行的產品表上以3.6秒的時間運行)
SELECT p.*, shop FROM products p JOIN
users u ON p.date >= u.prior_login and u.user_id = 22 JOIN
shops s ON p.shop_id = s.shop_id
ORDER BY shop, date, product_id;
這是解釋計划
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE u const PRIMARY,prior_login,user_id PRIMARY 4 const 1 Using temporary; Using filesort
1 SIMPLE s ALL PRIMARY NULL NULL NULL 90
1 SIMPLE p ref shop_id,date,shop_id_2,shop_id_3 shop_id 4 bitt3n_minxa.s.shop_id 5338 Using where
瓶頸似乎是ORDER BY date,product_id
。 刪除這兩個順序,查詢將在0.06秒內運行。 (刪除這兩者之一(但不能全部刪除)幾乎沒有任何效果,查詢仍然需要3秒鍾以上。)我在product表中同時具有product_id和date的索引。 我還添加了關於(產品,日期)的索引,但沒有改善。
newtover提示問題在於, INNER JOIN users u1 ON products.date >= u1.prior_login
要求阻止了在products.date上使用索引
已經向我建議了兩種查詢變體,它們在〜0.006秒(而不是原始的3.6秒)內執行(不是從該線程執行的)。
這使用了一個子查詢,該子查詢似乎強制了連接的順序
SELECT p.*, shop
FROM
(
SELECT p.*
FROM products p
WHERE p.date >= (select prior_login FROM users where user_id = 22)
) as p
JOIN shops s
ON p.shop_id = s.shop_id
ORDER BY shop, date, product_id;
此代碼使用WHERE子句執行相同的操作(盡管SQL_SMALL_RESULT的存在並不會更改執行時間,沒有SQL_SMALL_RESULT的執行時間也會更改為0.006秒)
SELECT SQL_SMALL_RESULT p . * , shop
FROM products p
INNER JOIN shops s ON p.shop_id = s.shop_id
WHERE p.date >= (
SELECT prior_login
FROM users
WHERE user_id =22 )
ORDER BY shop, DATE, product_id;
我的理解是,由於在將產品表連接到shops表之前減少了產品表的相關行數,因此這些查詢的工作速度更快。 我想知道這是否正確。
使用EXPLAIN
語句查看執行計划。 您也可以嘗試向products.date
和u1.prior_login
添加索引。
另外,請確保已定義外鍵並已對其進行索引。
祝好運。
我們確實需要一個解釋計划...但是
要非常小心,從表中的id中選擇* *(從another_table中選擇id)這是一個臭名昭著的事情。 通常,這些可以替換為聯接。 以下查詢可能會運行,盡管我尚未對其進行測試。
SELECT shop,
shops.shop_id AS shop_id,
products.product_id AS product_id,
brand,
title,
price,
image AS image,
image_width,
image_height,
0 AS sex,
products.date AS date,
fav1.favorited AS circle_favorited,
fav2.favorited AS session_user_favorited,
u2.username AS circle_username
FROM products
LEFT JOIN favorites fav2
ON fav2.product_id = products.product_id
AND fav2.user_id = 22
AND fav2.current = 1
INNER JOIN shops
ON shops.shop_id = products.shop_id
INNER JOIN users u1
ON products.date >= u1.prior_login AND u1.user_id = 22
LEFT JOIN favorites fav1
ON products.product_id = fav1.product_id
LEFT JOIN friends f1
ON f1.star_id = fav1.user_id
LEFT JOIN users u2
ON fav1.user_id = u2.user_id
WHERE f1.fan_id = 22 OR fav1.user_id = 22
ORDER BY shop,
DATE,
product_id,
circle_favorited
由於排序很慢,查詢很慢這一事實非常明顯,因為在這種情況下很難找到要應用ORDER BY的索引。 主要問題是products.date >=
比較,它使用ORDER BY的任何索引時都會中斷。 而且由於要輸出大量數據,MySQL開始使用臨時表進行排序。
我要做的是嘗試按已經具有所需順序的索引順序強制MySQL輸出數據,並刪除ORDER BY子句。
我不是要測試的計算機,但是我將如何做:
因此,問題是如何使數據在商店,日期,product_id上排序
我稍后再寫=)
UPD1:
您可能應該閱讀有關btree索引如何在MySQL中工作的內容。 mysqlperformanceblog.com上有一篇很好的文章(我目前在手機上撰寫,沒有鏈接可用)。 簡而言之,您似乎在談論單列索引,該索引基於在單列中排序的值來排列指向行的指針。 復合索引基於多個列存儲訂單。 在從索引所指向的行中檢索數據之前,大多數索引通常用於對其明確定義的范圍進行操作,以獲取大多數信息。 索引通常不知道同一張表上的其他索引,因此它們很少合並。 當索引中沒有更多信息時,MySQL開始直接對數據進行操作。
也就是說,日期索引不能使用product_id上的索引,但是日期日期后(針對特定日期匹配的產品ID排序),(日期,product_id)上的索引可以獲取有關product_id的更多信息。
但是,日期范圍條件(> =)打破了這一點。 那就是我在說的。
UPD2:
據我了解,這個問題可以減少到(大部分時間都花在此上):
SELECT p.*, shop
FROM products p
JOIN users u ON p.`date` >= u.prior_login and u.user_id = 22
JOIN shops s ON p.shop_id = s.shop_id
ORDER BY shop, `date`, product_id;
現在在用戶上添加索引(user_id,previous_login),在產品上添加索引(date),然后嘗試以下查詢:
SELECT STRAIGHT_JOIN p.*, shop
FROM (
SELECT product_id, shop
FROM users u
JOIN products p
user_id = 22 AND p.`date` >= prior_login
JOIN shops s
ON p.shop_id = s.shop_id
ORDER BY shop, p.`date`, product_id
) as s
JOIN products p USING (product_id);
如果我是正確的,查詢應該返回相同的結果,但速度更快。 如果可以,則將EXPLAIN的結果發布給查詢。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.