编辑:这是原始查询的简化版本(在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秒。)我在products表中的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的存在不会改变执行时间,没有它也会有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;
我的理解是,在将产品表的相关行数减少到商店表之前,这些查询的工作速度要快得多。我想知道这是否正确。
答案 0 :(得分:1)
使用EXPLAIN
语句查看执行计划。您也可以尝试向products.date
和u1.prior_login
添加索引。
另外请确保您已定义外键并将其编入索引。
祝你好运。答案 1 :(得分:0)
我们确实需要一个解释计划......但
要非常小心select * from table where where in(select from another_table)这是一个臭名昭着的。通常这些可以由连接替换。可能会运行以下查询,但我尚未对其进行测试。
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
答案 2 :(得分:0)
products.date >=
比较,它使用ORDER BY的任何索引中断。由于您需要输出大量数据,因此MySQL开始使用临时表进行排序。
我想尝试按照已经具有所需顺序的索引的顺序强制MySQL输出数据并删除ORDER BY子句。
我不是在电脑上测试,但我该怎么做:
所以,问题是如何在商店,日期,product_id
上对数据进行排序我稍后会写一下=)
UPD1:
您应该阅读有关btree索引如何在MySQL中工作的内容。有关mysqlperformanceblog.com的一篇很好的文章(我目前是从手机上写的,手边没有链接)。简而言之,您似乎谈论了一列索引,它根据在单个列中排序的值排列指向行的指针。复合索引基于多个列存储订单。索引主要用于在明确定义的范围内操作,以便在从它们指向的行检索数据之前获取大部分信息。索引通常不知道同一个表上的其他索引,因此它们很少被合并。当没有更多信息可以从索引中获取时,MySQL开始直接在数据上运行。
这是一个日期索引无法使用product_id上的索引,但是(date,product_id)上的索引可以在日期条件之后获得有关product_id的更多信息(对特定日期的产品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,prior_login)和产品上的(日期),并尝试以下查询:
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的结果会很好。