MYSQL - 左连接,优化2秒查询

时间:2011-08-30 10:08:14

标签: mysql performance query-optimization

我尝试优化我的MySQL查询,我已经将所有表合并到一个表似乎有用的表。

但查询仍需要2秒钟......有没有办法让它更快?

在user_id上始终是索引。 MySQL 5.5.12,除城市外的所有表都是InnoDB表。

SELECT b.user_id,b.firstname,b.lastname,b.address,b.zipcode,b.city
  , ($calculatings) AS Distance
FROM `user_profiles` `b`
LEFT JOIN `cities` `a` ON `a`.`postal`=`b`.`zipcode` 
JOIN `users` `u` ON `b`.`user_id`=`u`.`id` 
JOIN `user_consultants` `c` ON `b`.`user_id`=`c`.`user_id` 
WHERE ($calculatings) <= 25 
      AND c.incorporated='1' 
      AND u.typ='1' 
      AND u.activated='1' 
      AND u.banned='0' 
ORDER BY Distance ASC, c.zsum_score DESC 
LIMIT 30

在var $ calculates中是距离计算的数学运算(已经优化)。

$计算示例:

6368 * SQRT(2*(1-cos(RADIANS(`a`.`lat`)) * cos(0.840105508801) * 
(sin(RADIANS(`a`.`lon`)) * sin(0.201952047748) + cos(RADIANS(`a`.`lon`)) * 
cos(0.201952047748)) - sin(RADIANS(`a`.`lat`)) * sin(0.840105508801)))

为什么这么多左连接?

  1. user_profiles是用户信息的detail_table
  2. 城市是我的国家所有城市的城市表,有拉特和伦敦以及更多信息
  3. 用户是用户名和用户的主要用户表。密码,哈希,登录尝试,禁止等......
  4. user_consultants是特殊用户组的附加表
  5. 尺寸

    1. user_profiles 112.000行
    2. 城市68.000行
    3. 用户246.000行
    4. user_consultants 98.000行
    5. 解释SQL enter image description here(右键单击全尺寸)

5 个答案:

答案 0 :(得分:2)

我没有时间写完整的细节,但为了优化空间搜索,这里有一个快速的问题:

(latitude, longitude)对存储在(MyISAM)表中作为空间字段:POINTGEOMETRY类型的变体)。

在此字段上添加空间索引。

使用查询中的MBRContains()MBRWithin()函数,使用类似的方法,将使用空间索引缩小包含来自基数的Radius 25的圆的正方形内的搜索范围点:

WHERE MBRWithin( cities.myPointField
               , Polygon( @lat-25 @long-25
                        , @lat+25 @long-25
                        , @lat+25 @long+25
                        , @lat-25 @long+25
                        ) 
               )
  AND (yourDistanceCalculation) < 25

您可以查看MySQL文档: Spatial Extensions

答案 1 :(得分:1)

您应该拥有(至少)索引:cities.postal,users.typ,users.activated,users.banned,user_consultants.incorporated

答案 2 :(得分:1)

SELECT 
    b.user_id,b.firstname,b.lastname,b.address,b.zipcode,b.city,
    ($calculatings) AS Distance
FROM 
    `user_profiles` `b`
    JOIN `users` `u` ON `b`.`user_id`=`u`.`id`
     AND `u`.`typ`=1
     AND `u`.`activated`=1
     AND `u`.`banned`=0
    LEFT JOIN `cities` `a` ON `b`.`zipcode`=`a`.`postal` 
    LEFT JOIN `user_consultants` `c` ON `b`.`user_id`=`c`.`user_id` 
WHERE Distance <= 25
ORDER BY Distance ASC, c.zsum_score DESC
LIMIT 0,30

...虽然,如果$calculatings的值始终相同且我看到它只取决于表cities中的数据 - 您应该只在其中放入另一列,包含预先计算的距离值。

关于我所做的改变的一些注释:

  • 我只是假设typactivatedbanned的类型为int(通过查询中的值进行猜测) - 您不应将它们放在引号中
  • 我还假设由于users是您的主要用户表,user_id中的每个user_profiles都应该在id中存在users,所以你不要不需要LEFT
  • JOINWHERE条款快(WHEREHAVING快,因为我看到另一个使用它的答案。)
  • 正如都铎·康斯坦丁所回答的那样 - 你应该注意索引你用作连接参考的所有列。

答案 3 :(得分:1)

我认为你会略微偏离它(但可能是错的)。您正在根据用户配置文件开始查询,但您的所有条件都处于用户和用户顾问级别的最低级别。我会做一个STRAIGHT_JOIN(告诉优化器按照你声明的顺序)。然后,在您的连接上,执行LEFT JOIN并不一定有意义,除非您在表之间缺少链接ID值,这将允许某些记录不具有给定的城市或用户配置文件。所以,话虽如此,我会把你的用户表放在前面,因为这可能会通过标准设置最严格的结果集。另外,有一个索引(典型,激活,禁止)。接下来,您的user_consultants表并具有(user_id,合并)的索引。城市应该有邮政索引,user_profiles是邮政编码索引。

这是我要尝试的最终查询

select STRAIGHT_JOIN
      b.user_id,
      b.firstname,
      b.lastname,
      b.address,
      b.zipcode,
      b.city, 
      ($calculatings) AS Distance
   from 
      (select u.id, c.zsum_score
          from 
             users u
                join user_consultants c
                   on u.id = c.user_id
                  and c.incorporated = '1'
          where
                 u.typ = '1'
             and u.activated = '1'
             and u.banned = '0' ) PreQuery
      join user_profiles b
         on PreQuery.ID = b.user_id
         join cities a on b.zipcode = a.postal
   where
      ($calculatings) <= 25 
   ORDER BY
      Distance ASC, 
      PreQuery.zsum_score DESC 
   LIMIT 30

由于用户和用户顾问之间的连接是用户ID,因此用户顾问和用户配置文件是用户ID,“PreQuery”ID之间的连接是相同的,所以不需要重新连接到BOTH表

答案 4 :(得分:0)

SELECT 
    b.user_id,b.firstname,b.lastname,b.address,b.zipcode,b.city, 
    ($calculatings) AS Distance
FROM 
    `user_profiles` `b`
    JOIN `cities` `a` ON `a`.`postal`=`b`.`zipcode` 
    JOIN `users` `u` ON `b`.`user_id`=`u`.`id` 
    JOIN `user_consultants` `c` ON `b`.`user_id`=`c`.`user_id` 
WHERE     
    c.incorporated='1'  AND 
    u.typ='1' AND 
    u.activated='1' AND 
    u.banned='0' 
HAVING
    Distance <= 25
ORDER BY 
    Distance ASC, c.zsum_score DESC 
LIMIT 30