我可以避免使用子查询吗?

时间:2017-06-22 19:45:22

标签: mysql sql join subquery

我的结构很简单: 我ways由多个points组成。每个point都有x和y位置。 方法是在额外的表wayspoints的帮助下定义的,其中包含widpid列。

我要做的是在给定的x和y的一定距离内获取所有方式(包含所有点)。

我通过检查点x,y和给定的x,y之间的差异来做到这一点。

我遇到的问题是我不想要距离内的所有点,而是所有属于距离内各点都有的点。

在我有这个要求之前,我的查询运行了大约100毫秒,获得了100万点。由于我现在需要各自的方式,我添加了一个子查询,现在需要大约4-5秒来获取。

这对我来说是不可接受的。这是sql(数字是给定的x和y坐标):

SELECT concat(WAYS.id, '|',WAYS.name) "id", POINTS.x, POINTS.y 
FROM WAYS, WAYPOINTS, POINTS 
WHERE WAYPOINTS.WID = WAYS.ID AND 
      WAYPOINTS.PID = POINTS.ID AND 
      WAYPOINTS.WID IN (SELECT w.ID 
                        FROM WAYS w, WAYPOINTS wp, POINTS p 
                        WHERE wp.WID = w.ID AND wp.PID = p.ID 
                        AND p.x < (51.739400 + 0.01) 
                        AND p.x > (51.739400 - 0.01)
                        AND p.y < (8.710202 + 0.01)
                        AND p.y > (8.710202 - 0.01);

如果我至少可以达到最大1秒,这将是不错的。我知道我问了很多,因为解决方案已经开始了,但我觉得必须有一些我在这里缺少的东西..

PS:我已经为每个列添加了一个索引。

4 个答案:

答案 0 :(得分:1)

SELECT p.*
FROM points AS pWithin
INNER JOIN waypoints AS pwWP ON pWithin.ID = pwWP.PID
INNER JOIN way AS pwW ON pwWP.WID = pwW.ID
INNER JOIN waypoints AS wp ON pwW.ID = wp.WID
INNER JOIN points AS p ON wp.PID = p.ID
WHERE pWithin.x < (51.739400 + 0.01) 
   AND pWithin.x > (51.739400 - 0.01)
   AND pWithin.y < (8.710202 + 0.01)
   AND pWithin.y > (8.710202 - 0.01);

WHEREpoints的第一个引用找到“距离内的点”,接下来的两个连接用于查找这些点的方式,最后两个连接找到点在这些方面。

显然,您可以将p.*替换为您想要的实际结果数据。

编辑:您可能需要注意的一件事是,如果way在距离内有多个点,那么它的所有点都将显示多次。只需将SELECT更改为SELECT DISTINCT即可轻松解决问题。

答案 1 :(得分:0)

是的,您是否尝试将该子查询创建为CTE?

例如,您可以编写类似

的内容
WITH (SELECT w.ID 
                    FROM WAYS w, WAYPOINTS wp, POINTS p 
                    WHERE wp.WID = w.ID AND wp.PID = p.ID 
                    AND p.x < (51.739400 + 0.01) 
                    AND p.x > (51.739400 - 0.01)
                    AND p.y < (8.710202 + 0.01)
                    AND p.y > (8.710202 - 0.01) as CTE
SELECT concat(WAYS.id, '|',WAYS.name) "id", POINTS.x, POINTS.y 
FROM WAYS, WAYPOINTS, POINTS, cte
WHERE WAYPOINTS.WID = WAYS.ID AND 
  WAYPOINTS.PID = POINTS.ID AND 
  WAYPOINTS.WID = cte.id

您需要将查询的效果与CTE查询进行比较;

这样做你就会写

EXPLAIN ANALYZE ( 
*the entire query*
);

答案 2 :(得分:0)

不正确这就是你要问的。 试试这个。你应该只获得那些点标准为真的Waypoint。

 SELECT concat(WAYS.id, '|',WAYS.name) "id", POINTS.x, POINTS.y 
    FROM WAYS, WAYPOINTS, POINTS 
    WHERE WAYPOINTS.WID = WAYS.ID AND 
          WAYPOINTS.PID = POINTS.ID AND 
          AND Points.x < (51.739400 + 0.01) 
          AND points.x > (51.739400 - 0.01)
          AND points.y < (8.710202 + 0.01)
          AND points.y > (8.710202 - 0.01);

答案 3 :(得分:0)

替代方法:使用exists()加入桥接表。这样可以避免最终结果出现重复。

SELECT concat(w.id, '|',w.name) "id", p.x, p.y 
FROM WAYS w
JOIN POINTS p ON EXISTS ( -- Join on bridge-table
    SELECT * 
    FROM  WAYPOINTS wp
    WHERE wp.WID = w.ID 
    AND wp.PID = p.ID
    AND EXISTS ( -- waypoints that contain a particular point
            SELECT*
            FROM WAYPOINTS wp2
            JOIN  POINTS p2 ON wp2.PID = p2.ID
            WHERE wp2.WID = wp.WID -- same way as outer
            AND p2.x < 51.739400 + 0.01
            AND p2.x > 51.739400 - 0.01
            AND p2.y < 8.710202 + 0.01
            AND p2.y > 8.710202 - 0.01
            )
    );