我有一个查询,它有一些子查询(内部选择),我正在尝试解决哪个更好的性能,一个更大的查询或许多小查询,我发现很难尝试和时间差异因为它在我的服务器上一直在变化。
我使用下面的查询一次返回10个结果,以便在我的网站上显示,使用分页(偏移和限制)。
SELECT adverts.*, breed.breed, breed.type, sellers.profile_name, sellers.logo, users.user_level ,
round( sqrt( ( ( (adverts.latitude - '51.558430') * (adverts.latitude - '51.558430') ) * 69.1 * 69.1 ) + ( (adverts.longitude - '-0.0069345') * (adverts.longitude - '-0.0069345') * 53 * 53 ) ), 1 ) as distance,
( SELECT advert_images.image_name FROM advert_images WHERE advert_images.advert_id = adverts.advert_id AND advert_images.main = 1 LIMIT 1) as imagename,
( SELECT count(advert_images.advert_id) from advert_images WHERE advert_images.advert_id = adverts.advert_id ) AS num_photos
FROM adverts
LEFT JOIN breed ON adverts.breed_id = breed.breed_id
LEFT JOIN sellers ON (adverts.user_id = sellers.user_id)
LEFT JOIN users ON (adverts.user_id = users.user_id)
WHERE (adverts.status = 1) AND (adverts.approved = 1)
AND (adverts.latitude BETWEEN 51.2692837281 AND 51.8475762719) AND (adverts.longitude BETWEEN -0.472015213613 AND 0.458146213613)
having (distance <= '20')
ORDER BY distance ASC
LIMIT 0,10
从主查询中删除下面的2个内部选择会不会更好,然后在我的php循环中,调用2次选择10次,循环中的每个记录一次?
( SELECT advert_images.image_name FROM advert_images WHERE advert_images.advert_id = adverts.advert_id AND advert_images.main = 1 LIMIT 1) as imagename,
( SELECT count(advert_images.advert_id) from advert_images WHERE advert_images.advert_id = adverts.advert_id ) AS num_photos
答案 0 :(得分:1)
据我了解你的内心选择,它们有两个目的:找到关联图像的任何名称,计算关联图像的数量。您可能可以使用左连接而不是内部选择来实现两者:
SELECT …,
advert_images.image_name AS imagename,
COUNT(advert_images.advert_id) AS num_photos,
…
FROM …
LEFT JOIN advert_images ON advert_images.advert_id = adverts.advert_id
…
GROUP BY adverts.advert_id
…
LIMIT 0,10
我没有尝试过这个,但是MySQL引擎可能足够智能,只能为你实际返回的行执行查询部分。
请注意,对于,对于给定的一组图像,此查询将返回哪个图像名称,根本无法保证。如果你想要可重复的结果,你应该在那里使用一些聚合函数,例如MIN(advert_images.image_name)
选择按字典顺序排列的第一张图片。
如果上述方法不起作用,即查询仍将检查计算结果的所有行的advert_images
表,那么通过执行第二行可能会更好查询。但是,您可以尝试避免for
循环,而是在单个查询中获取所有这些行:
SELECT advert_images.image_name AS imagename,
COUNT(advert_images.advert_id) AS num_photos
FROM advert_images
WHERE advert_images.advert_id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
GROUP BY advert_images.advert_id
此查询中的十个参数对应于您当前生成的十行结果。请注意,根据该结果,没有相关照片的广告将不会包含在该结果中。因此,请确保在代码中将num_photos
默认为零并imagename
为NULL
。
实现您尝试执行的操作的另一种方法是使用显式的临时内存表:首先选择您感兴趣的结果,然后检索所有相关信息。
CREATE TEMPORARY TABLE tmp
SELECT adverts.advert_id, round(…) as distance
FROM adverts
WHERE (adverts.status = 1) AND (adverts.approved = 1)
AND (adverts.latitude BETWEEN 51.2692837281 AND 51.8475762719)
AND (adverts.longitude BETWEEN -0.472015213613 AND 0.458146213613)
HAVING (distance <= 20)
ORDER BY distance ASC
LIMIT 0,10;
SELECT tmp.distance, adverts.*, …
advert_images.image_name AS imagename,
COUNT(advert_images.advert_id) AS num_photos,
…
FROM tmp
INNER JOIN adverts ON tmp.advert_id = adverts.advert_id
LEFT JOIN breed ON adverts.breed_id = breed.breed_id
LEFT JOIN sellers ON adverts.user_id = sellers.user_id
LEFT JOIN users ON adverts.user_id = users.user_id
LEFT JOIN advert_images ON advert_images.advert_id = adverts.advert_id
GROUP BY adverts.advert_id
ORDER BY tmp.distance ASC;
DROP TABLE tmp;
这将确保仅查询您当前正在处理的结果的所有其他表。毕竟,advert_images
表没有什么神奇之处,除了你可能需要多行的事实。
基于前一段的方法,您甚至可以避免管理临时表,并使用子查询代替:
SELECT sub.distance, adverts.*, …
advert_images.image_name AS imagename,
COUNT(advert_images.advert_id) AS num_photos,
…
FROM ( SELECT adverts.advert_id, round(…) as distance
FROM adverts
WHERE (adverts.status = 1) AND (adverts.approved = 1)
AND (adverts.latitude BETWEEN 51.2692837281 AND 51.8475762719)
AND (adverts.longitude BETWEEN -0.472015213613 AND 0.458146213613)
HAVING (distance <= 20)
ORDER BY distance ASC
LIMIT 0,10;
) AS sub
INNER JOIN adverts ON sub.advert_id = adverts.advert_id
LEFT JOIN breed ON adverts.breed_id = breed.breed_id
LEFT JOIN sellers ON (adverts.user_id = sellers.user_id)
LEFT JOIN users ON (adverts.user_id = users.user_id)
LEFT JOIN advert_images ON advert_images.advert_id = adverts.advert_id
GROUP BY adverts.advert_id
ORDER BY sub.distance ASC
同样,您只能使用adverts
表中的数据来确定相关行,并且只加入其他表中所需的行。最有可能的是,该中间结果将在内部存储在临时表中,但这取决于SQL服务器的决定。
答案 1 :(得分:0)
我认为MySQL使用文件排序+临时表来执行查询。这就是为什么在大牌桌上你的建议会带来更好的结果。一般来说,你最好执行较小的查询然后1大。