【问题标题】:Optimize calculations in this query?优化此查询中的计算?
【发布时间】:2015-03-21 18:37:42
【问题描述】:

优化此查询的最佳方法是什么?

$tripsNearLocation = mysqli_query($con, 
   "SELECT * FROM (
       SELECT *
       , ( 3959 * acos( cos(" . $latRad . ") 
         * cos( radians( startingLatitude ) ) 
         * cos( radians( startingLongitude ) 
         - (" . $longRad . ") ) 
         + sin(" . $latRad . ") 
         * sin( radians( startingLatitude ) ) ) ) 
         AS distance FROM trips
      ) as query 
   WHERE distance < 10 
   ORDER BY distance LIMIT 0 , 10;");

如果有 50,000 行,则需要一两秒才能完成。我是否应该添加一个不同的查询来消除甚至不在输入坐标的“近距离”范围内的所有行,然后计算剩余的行?假设输入的纬度坐标是 67,则排除纬度坐标不是 65-69 的所有行。

或者添加一个“状态列”,如果它们不在同一状态,它会从计算中删除所有行?

还是只处理 2 秒的计算?我担心数据库可能包含超过 100,000 行,并且执行起来需要很长时间。

【问题讨论】:

    标签: mysql performance optimization query-optimization


    【解决方案1】:

    A 计划:对于 100K 行,您可能只需按纬度缩小范围即可。也就是说,

    • 计算与“10”个距离单位相对应的纬度
    • 有 INDEX(startingLatitude)
    • 添加到 WHERE 子句以将其限制为起始纬度加/减“10”。也许你的例子是AND startingLatitude BETWEEN 65 AND 69

    如果您正在考虑使用 INDEX(lat, lng),那就没那么简单了。看看拉特是否足够好。

    计划 B:下一个选择将涉及 lat 和 lng,以及一个子查询。 5.6 版将是有益的。是这样的(包括INDEX(lat, lng, id)之后):

    SELECT ... FROM (
        SELECT id FROM tbl
            WHERE lat BETWEEN... 
              AND lng BETWEEN... ) x
        JOIN tbl USING (id)
        WHERE ...;
    

    由于种种原因,Plan B 只比 Plan A 稍微好一点。

    计划 C:如果您需要数百万行,则需要 my pizza parlor algorithm。这涉及一个存储过程来重复探测,寻找足够的行。它还涉及PARTITIONing 以获得粗略的 2D 索引。

    计划A和B是O(sqrt(N)); C计划是O(1)。也就是说,对于计划 A 和 B,如果将行数增加四倍,则花费的时间会增加一倍。 C计划并没有变慢。 (听起来你的代码是O(N)——行数加倍=时间加倍。)

    【讨论】:

    • 我想我可以通过纬度和经度使用计划 a 来缩小范围。这样也行吗?
    • 这不是那么简单,因为优化器只会查看一个范围,而不是两个。我会编辑答案。
    • 答案大部分是正确的,但完全忽略了空间索引,这在大多数 mysql 实现中作为标准提供,并且会提供更快和更准确的结果。
    • @symcbean - 请使用SPATIAL 提供答案。
    【解决方案2】:

    这就是我最终解决它的方式,以防人们将来需要参考。

    $tripsNearLocation = mysqli_query($con, "SELECT * FROM (
    SELECT *, (3959 * acos(cos(" . $latRad . ") * cos(radians(startingLatitude)) 
    * cos(radians(startingLongitude) - (" . $longRad . ")) + sin(" . $latRad . ") 
    * sin(radians(startingLatitude)))) AS distance FROM (
    SELECT * FROM trips_test WHERE startingLatitude BETWEEN " .
    ($locationLatitude - 1) . " AND " . ($locationLatitude + 1) . ") as query1) 
    as query2 WHERE distance < 10 ORDER BY distance LIMIT 0 , 10;");
    

    虽然我会接受 Rick James 的回答,因为他帮助我解决了这个问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-01
      相关资源
      最近更新 更多