MYSQL查询优化,多查询或一个大查询

use*_*096 5 mysql optimization performance select query-optimization

我有一个查询,它有一些子查询(内部选择),我正在尝试找出哪个对性能更好,一个更大的查询或许多更小的查询,我发现很难在差异变化时尝试和计时一直在我的服务器上。

我使用下面的查询一次返回 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
Run Code Online (Sandbox Code Playgroud)

从主查询中删除下面的 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 
Run Code Online (Sandbox Code Playgroud)

MvG*_*MvG 2

避免子查询

\n\n

据我了解您的内心选择,它们有两个目的:查找关联图像的任何名称,并计算关联图像的数量。您可能可以使用左连接而不是内部选择来实现这两者:

\n\n
SELECT \xe2\x80\xa6,\n      advert_images.image_name AS imagename,\n      COUNT(advert_images.advert_id) AS num_photos,\n      \xe2\x80\xa6\nFROM \xe2\x80\xa6\n     LEFT JOIN advert_images ON advert_images.advert_id = adverts.advert_id\n\xe2\x80\xa6\nGROUP BY adverts.advert_id\n\xe2\x80\xa6\nLIMIT 0,10\n
Run Code Online (Sandbox Code Playgroud)\n\n

我还没有尝试过这个,但也许 MySQL 引擎足够聪明,只能对您实际返回的行执行那部分查询。

\n\n

请注意,根本无法保证此查询将为给定的图像集返回哪个图像名称。如果您想要可重复的结果,您应该在那里使用一些聚合函数,例如MIN(advert_images.image_name)选择字典顺序的第一个图像。

\n\n

单独选择但不循环

\n\n

如果上述方法不起作用,即查询仍将检查表中advert_images计算结果的所有行,那么执行第二个查询可能会更好。但是,您可以尝试避免for循环,而是在单个查询中获取所有这些行:

\n\n
SELECT advert_images.image_name AS imagename,\n       COUNT(advert_images.advert_id) AS num_photos\nFROM advert_images\nWHERE advert_images.advert_id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\nGROUP BY advert_images.advert_id\n
Run Code Online (Sandbox Code Playgroud)\n\n

此查询中的十个参数对应于您当前生成的结果的十行。请注意,没有相关照片的广告根本不会包含在该结果中。因此,请确保在代码中默认设置num_photos为零和imagenameto 。NULL

\n\n

临时表

\n\n

实现您尝试执行的操作的另一种方法是使用显式临时内存表:首先选择您感兴趣的结果,然后检索所有关联的信息。

\n\n
CREATE TEMPORARY TABLE tmp\nSELECT adverts.advert_id, round(\xe2\x80\xa6) as distance\nFROM adverts\nWHERE (adverts.status = 1) AND (adverts.approved = 1)\n  AND (adverts.latitude BETWEEN 51.2692837281 AND 51.8475762719)\n  AND (adverts.longitude BETWEEN -0.472015213613 AND 0.458146213613)\nHAVING (distance <= 20)\nORDER BY distance ASC\nLIMIT 0,10;\n\nSELECT tmp.distance, adverts.*, \xe2\x80\xa6\n       advert_images.image_name AS imagename,\n       COUNT(advert_images.advert_id) AS num_photos,\n       \xe2\x80\xa6\nFROM tmp\n     INNER JOIN adverts ON tmp.advert_id = adverts.advert_id\n     LEFT JOIN breed ON adverts.breed_id = breed.breed_id\n     LEFT JOIN sellers ON adverts.user_id = sellers.user_id\n     LEFT JOIN users ON adverts.user_id = users.user_id\n     LEFT JOIN advert_images ON advert_images.advert_id = adverts.advert_id\nGROUP BY adverts.advert_id\nORDER BY tmp.distance ASC;\n\nDROP TABLE tmp;\n
Run Code Online (Sandbox Code Playgroud)\n\n

这将确保仅查询所有其他表以获取您当前正在处理的结果。advert_images毕竟,除了您可能需要其中的多行之外,该表没有什么神奇之处。

\n\n

子查询作为连接因子

\n\n

基于上一段的方法,您甚至可以避免管理临时表,并使用子查询来代替:

\n\n
SELECT sub.distance, adverts.*, \xe2\x80\xa6\n       advert_images.image_name AS imagename,\n       COUNT(advert_images.advert_id) AS num_photos,\n       \xe2\x80\xa6\nFROM ( SELECT adverts.advert_id, round(\xe2\x80\xa6) as distance\n        FROM adverts\n        WHERE (adverts.status = 1) AND (adverts.approved = 1)\n          AND (adverts.latitude BETWEEN 51.2692837281 AND 51.8475762719)\n          AND (adverts.longitude BETWEEN -0.472015213613 AND 0.458146213613)\n        HAVING (distance <= 20)\n        ORDER BY distance ASC\n        LIMIT 0,10;\n     ) AS sub\n     INNER JOIN adverts ON sub.advert_id = adverts.advert_id\n     LEFT JOIN breed ON adverts.breed_id = breed.breed_id \n     LEFT JOIN sellers ON (adverts.user_id = sellers.user_id) \n     LEFT JOIN users ON (adverts.user_id = users.user_id) \n     LEFT JOIN advert_images ON advert_images.advert_id = adverts.advert_id\nGROUP BY adverts.advert_id\nORDER BY sub.distance ASC\n
Run Code Online (Sandbox Code Playgroud)\n\n

您再次仅使用表中的数据来确定相关行adverts,并仅连接其他表中所需的行。最有可能的是,中间结果将在内部存储在临时表中,但这由 SQL Server 决定。

\n