mysql 使用内部连接优化查询,其中和排序依据和坐标

Posted

技术标签:

【中文标题】mysql 使用内部连接优化查询,其中和排序依据和坐标【英文标题】:mysql optimise a query with inner join where and order by and co-ordinates 【发布时间】:2016-04-11 17:32:27 【问题描述】:

我只是想掌握分析。

我之前优化了一个简单的查询,但这个查询涉及更多,所以我不确定在这种情况下我可能需要向哪些列添加索引!

SELECT ReportID, Title, Description, postPic , DatePosted, UserID, FName, SName, authorNotificationId, userPic,  Lat, `Long`, 
photoWidth, photoHeight 
FROM 
(SELECT
        (((acos(sin((?*pi()/180)) 
        * sin((`Lat`*pi()/180))+cos((?*pi()/180))
        * cos((`Lat`*pi()/180)) * cos(((?- `Long`)
        *pi()/180))))*180/pi())*60*1.1515*1609.34)
        AS distance, Title, Description, posts.Pic as postPic, ReportID, DatePosted, posts.UserID as UserID, FName, SName, NotificationID as authorNotificationId, users.Pic as userPic,
        Lat, `Long`, photoWidth, photoHeight
        FROM posts
        INNER JOIN Users 
        ON Users.UserID = posts.UserID) foo
        WHERE distance <=? AND (Title LIKE ? OR Description LIKE ? OR FName LIKE ? OR SName LIKE ?) 
ORDER BY DatePosted DESC LIMIT ?, 10

查询的简要说明:它尝试检索用户预定义半径内的所有帖子 - 通过检查用户注册位置的坐标与帖子的关联坐标之间的距离(并检查该距离是否小于用户注册的半径)

运行解释给了我以下信息:

id             : 1
select_type    : Primary
table          : <derived2>
type           : **ALL**
possible_keys  : NULL
key            : NULL
key_len        : NULL
ref            : NULL
rows           : 824
Extra          : Using where; **Using filesort**

id             : 2
select_type    : DERIVED
table          : tableposts
type           : ALL
possible_keys  : NULL
key            : NULL
key_len        : NULL
ref            : NULL
rows           : 824
Extra          : NULL

id             : 2
select_type    : DERIVED
table          : Users
type           : eq_ref
possible_keys  : PRIMARY
key            : PRIMARY
key_len        : 4
ref            : db.posts.UserID
rows           : 1
Extra          : NULL

由于 ALL select_type 和 filesort 在额外的情况下,我只需要担心查询 1 和 2。

是否必须为列标题和描述添加索引?我已经在 users 表中建立了 fname 和 sname 的索引。

正如 Olli 所指出的,距离是计算的结果,所以我无法为其添加索引

更新

从 Olli 的回答中,我从 EXPLAIN 中得到以下信息:

id           : 1
select_type  : SIMPLE
table        : posts
type         : ALL
possible_keys: postsUserID_idx
key          : NULL
key_len      : NULL
ref          : NULL
rows         : 825
Extra        : Using where; Using filesort

id           : 1
select_type  : SIMPLE
table        : Users
type         : eq_ref
possible_keys: PRIMARY
key          : PRIMARY
key_len      : 4
ref          : db.posts.UserID
rows         : 1
Extra        : Using where

更新 2:

根据 symcbean 的建议,最初尝试重新构造表以添加另一个类型为 point 的 col:

set @lat= 53.277656872106;
set @lon = -9.01179972749435;
set @dist = CONV_MI_KM(50000, 'k')/1000;
set @rlon1 = @lon-@dist/abs(cos(radians(@lat))*69);
set @rlon2 = @lon+@dist/abs(cos(radians(@lat))*69);
set @rlat1 = @lat-(@dist/69);
set @rlat2 = @lat+(@dist/69);

//set @geoCol = astext(envelope(linestring(point(@rlon1, @rlat1), point(@rlon2, @rlat2))));
set @geoCol = astext(linestring(point(@rlon1, @rlat1), point(@rlon2, @rlat2)));
set @geoCol2 = linestring(point(@rlon1, @rlat1), point(@rlon2, @rlat2));

我需要更改我的注册准备语句:

INSERT INTO users VALUES 
    ('', ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, 0, ?, 0)

在 0 之后的表末尾包含 newCol。这应该是我定义为 geoCol2 的内容吗?不确定的原因是在php中绑定参数时我认为我可能需要使用“s”作为绑定类型,因为在查找可以绑定到语句的参数类型时,我只看到s,i, d 和 b 作为选项。还是我跑题了?

【问题讨论】:

当我没看错时,您的列距是计算的结果吗?如果是这样,我看不到实际添加索引的方法。 @Olli 对不起,是的,你是对的。还有其他优化建议吗? 该子选择有什么原因吗?您是否尝试过简单地使用连接(没有子选择)来编写整个查询? 使用地理空间索引在边界框中获取结果,然后如果您确实需要半径,请在结果集上进行触发。 @Olli 我几个月前写的,但我记得这是我当时让它工作的唯一方法!我不断收到错误,我不记得具体细节,但子查询是我可以管理的,但也许我不需要它?? 【参考方案1】:

您是以英里还是公里计算距离?

你能不能试试这个查询(应该是一样的,只是没有子查询):

SELECT 
  (((acos(sin((?*pi()/180)) 
    * sin((`Lat`*pi()/180))+cos((?*pi()/180))
    * cos((`Lat`*pi()/180)) * cos(((?- `Long`)
    *pi()/180))))*180/pi())*111189.3006) AS distance,
  ReportID, 
  Title, 
  Description, 
  postPic , 
  DatePosted, 
  UserID, 
  FName, 
  SName, 
  authorNotificationId, 
  userPic,  
  Lat, 
  `Long`, 
  photoWidth, 
  photoHeight 
FROM
  posts 
JOIN Users ON (Users.UserID = posts.UserID) 
WHERE (Title LIKE ? OR Description LIKE ? OR FName LIKE ? OR SName LIKE ?) 
HAVING distance <= ?
ORDER BY 
  DatePosted DESC LIMIT ?, 10

由于您即时计算距离,因此您需要 HAVING 子句进行检查,否则使用 WHERE 会因未知列而失败。

也许你也可以为这个添加执行计划?

【讨论】:

非常感谢您简化了查询!使用您的方法,执行时间已从 0.0177 下降到 0.0020 :) 但是,当我对您的查询运行解释时,它仍然具有选择类型 ALL 并在第一个查询中使用文件排序。我将在更新中添加解释结果 对不起,我跳过了!我在有条件下比较距离的半径 val 以米为单位存储,因此传入的参数是从 0 到 50000 酷,至少有些东西.. 仍然是问题:您的距离是公里还是英里?您可以通过定义纬度/经度值的范围来限制可搜索的帖子,您还可以在这些列上使用索引。这将减少实际计算的行数。但老实说.. 您查询的时间非常好,所以我不会太担心文件排序... ;-) 它的单位是米而不是公里或英里 :) 感谢您的帮助。是的,我认为文件排序不是什么大问题,但这就是我担心的全部。现在时间很好,因为没有大量的行,但如果表变得非常大,那么我会担心。当你说为我的 lat long 值定义一个范围时,你到底是什么意思?添加新列?并在该列上放置然后放置一个索引? 你好,一点也不着急,(因为我不会在几周内实施),但如果你有机会并且愿意,如果你愿意,那就太好了可以查看更新后的问题,看看我是否沿着正确的路线尝试并实施允许我使用地理空间索引和边界框的解决方案。

以上是关于mysql 使用内部连接优化查询,其中和排序依据和坐标的主要内容,如果未能解决你的问题,请参考以下文章

复合索引顺序 MySQL 查询

SQL Jet 访问、插入列和排序依据

MySQL高级第八篇:关联查询子查询和排序相关优化

这两种 MySQL 查询形式总是等价的吗?分组依据 + 排序依据 + 限制

分组依据/排序依据的 MySQL 索引

那个mysql 子查询和连接查询 一般常用哪个 谁效率高些