优化此查询中的计算?
Optimize calculations in this query?
优化此查询的最佳方法是什么?
$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 行需要一两秒才能完成。我是否应该添加一个不同的查询来消除所有甚至不在输入坐标的 "close range" 中的行,然后计算剩余的行?假设输入的纬度坐标是 67,则消除纬度坐标不是 65-69 的所有行。
或者添加一个 "state column",如果它们处于不同的状态,它会从计算中删除所有行?
还是只处理这2秒的计算?我担心数据库可能包含超过 100,000 行并且需要很长时间才能执行。
方案 A:对于 100K 行,您可能只需按纬度缩小范围即可。也就是说,
- 计算对应于“10”个距离单位的纬度
- 有 INDEX(startingLatitude)
- 添加到 WHERE 子句以将其限制为 startingLatitude plus/minus“10”。也许你的例子是
AND startingLatitude BETWEEN 65 AND 69
.
如果您正在考虑使用 INDEX(lat, lng),那就没那么简单了。看看 Lat 是否足够好。
Plan 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 ...;
由于种种原因,方案B仅略优于方案A。
计划 C:如果您需要数百万行,则需要 my pizza parlor algorithm。这涉及一个存储过程来反复探测,寻找足够的行。它还涉及 PARTITION
获取粗略的 2D 索引。
方案A和方案B是O(sqrt(N))
; C计划是O(1)
。也就是说,对于计划 A 和 B,如果将行数增加四倍,则花费的时间也会增加一倍。 Plan C 不会变慢。 (听起来你的代码是 O(N)
-- 行数加倍 = 时间加倍。)
这就是我最终解决它的方式,以防将来人们需要参考它。
$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 的回答,因为他帮助我找到了这个解决方案。
优化此查询的最佳方法是什么?
$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 行需要一两秒才能完成。我是否应该添加一个不同的查询来消除所有甚至不在输入坐标的 "close range" 中的行,然后计算剩余的行?假设输入的纬度坐标是 67,则消除纬度坐标不是 65-69 的所有行。
或者添加一个 "state column",如果它们处于不同的状态,它会从计算中删除所有行?
还是只处理这2秒的计算?我担心数据库可能包含超过 100,000 行并且需要很长时间才能执行。
方案 A:对于 100K 行,您可能只需按纬度缩小范围即可。也就是说,
- 计算对应于“10”个距离单位的纬度
- 有 INDEX(startingLatitude)
- 添加到 WHERE 子句以将其限制为 startingLatitude plus/minus“10”。也许你的例子是
AND startingLatitude BETWEEN 65 AND 69
.
如果您正在考虑使用 INDEX(lat, lng),那就没那么简单了。看看 Lat 是否足够好。
Plan 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 ...;
由于种种原因,方案B仅略优于方案A。
计划 C:如果您需要数百万行,则需要 my pizza parlor algorithm。这涉及一个存储过程来反复探测,寻找足够的行。它还涉及 PARTITION
获取粗略的 2D 索引。
方案A和方案B是O(sqrt(N))
; C计划是O(1)
。也就是说,对于计划 A 和 B,如果将行数增加四倍,则花费的时间也会增加一倍。 Plan C 不会变慢。 (听起来你的代码是 O(N)
-- 行数加倍 = 时间加倍。)
这就是我最终解决它的方式,以防将来人们需要参考它。
$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 的回答,因为他帮助我找到了这个解决方案。