gpt4 book ai didi

php - Haversine 公式中的连接操作

转载 作者:可可西里 更新时间:2023-11-01 07:24:53 25 4
gpt4 key购买 nike

我正在用 PHP 实现 Haversine 公式,如下所示

$result=mysqli_query($mysqli,"SELECT *,( 6371 * acos( cos( radians({$lat}) ) * cos( radians( `latitude` ) ) * cos( radians( `longitude` ) -radians({$lon}) ) +sin( radians({$lat}) ) * sin( radians( `latitude` ) ) ) ) AS distance FROM `places` HAVING distance <= {$radius} ORDER BY distance ASC") or die(mysqli_error($mysqli));

并且在 Haversine 提取循环中,我有一个查询,它遍历 haversine 的结果以选择与 haversine 公式返回的 ID 匹配的记录。查询如下。

 while($row = mysqli_fetch_assoc($result)) 

{
$rest_time=$row['id'];

$result1=mysqli_query($mysqli,"SELECT * FROM my_friends WHERE personal_id='".$personal_id."' AND id='".$rest_time."'") or die(mysqli_error($mysqli));

//Some operations here
}

如何执行 Join 操作将这些查询混合成一个查询?如果第二个表有 50k 个用户而第一个表有近 1000 条记录,从优化的角度来看这样做是否明智?

最佳答案

您在此处对所有行执行的任何操作对于那么多记录都会很慢。

您需要做的是利用索引。要使用索引,它必须是一个简单的查询而不是 the result of a function (目前的情况)。

通过半径搜索所做的就是围绕一个点做一个圆,通过在做圆之前使用一些触发器,我们可以得出以下结果

Circle two squares

其中S1是里面最大的正方形,S2是外面最小的正方形。

现在我们可以算出这两个正方形的尺寸,S2 外部的任何东西都被索引命中,S1 内部的任何东西都被索引命中,只留下现在需要查找的小区域内部慢速法。

如果您需要与点的距离,请忽略 S1 部分(因为圆内的所有内容都需要 haversine 函数)作为此处的注释,虽然圆内的所有内容都需要它,但并非每个点都在距离内,所以WHERE 子句仍然需要

所以让我们用单位圆来计算这些点 Unit Circle

function getS1S2($latitude, $longitude, $kilometer)
{
$radiusOfEarthKM = 6371;
$latitudeRadians = deg2rad($latitude);
$longitudeRadians = deg2rad($longitude);
$distance = $kilometer / $radiusOfEarthKM;

$deltaLongitude = asin(sin($distance) / cos($latitudeRadians));

$bounds = new \stdClass();

// these are the outer bounds of the circle (S2)
$bounds->minLat = rad2deg($latitudeRadians - $distance);
$bounds->maxLat = rad2deg($latitudeRadians + $distance);
$bounds->minLong = rad2deg($longitudeRadians - $deltaLongitude);
$bounds->maxLong = rad2deg($longitudeRadians + $deltaLongitude);

// and these are the inner bounds (S1)
$bounds->innerMinLat = rad2deg($latitudeRadians + $distance * cos(5 * M_PI_4));
$bounds->innerMaxLat = rad2deg($latitudeRadians + $distance * sin(M_PI_4));
$bounds->innerMinLong = rad2deg($longitudeRadians + $deltaLongitude * sin(5 * M_PI_4));
$bounds->innerMaxLong = rad2deg($longitudeRadians + $deltaLongitude * cos(M_PI_4));

return $bounds;
}

现在你的查询变成了

SELECT 
*
FROM
`places`
HAVING p.nlatitude BETWEEN {$bounds->minLat}
AND {$bounds->maxLat}
AND p.nlongitude BETWEEN {$bounds->minLong}
AND {$bounds->maxLong}
AND (
(
p.nlatitude BETWEEN {$bounds->innerMinLat}
AND {$bounds->innerMaxLat}
AND p.nlongitude BETWEEN {$bounds->innerMinLong}
AND {$bounds->innerMaxLong}
)
OR (
6371 * ACOS(
COS(RADIANS({ $lat })) * COS(RADIANS(`latitude`)) * COS(
RADIANS(`longitude`) - RADIANS({ $lon })
) + SIN(RADIANS({ $lat })) * SIN(RADIANS(`latitude`))
)
)
)) <= {$radius}
ORDER BY distance ASC

重要

上面有可读性的文本,请确保这些值正确转义/最好参数化

这样就可以利用索引,让连接在更快的时间内发生

加入连接这就变成了

SELECT 
*
FROM
`places` p
INNER JOIN my_friends f ON f.id = p.id
WHERE p.latitude BETWEEN {$bounds->minLat}
AND {$bounds->maxLat}
AND p.longitude BETWEEN {$bounds->minLong}
AND {$bounds->maxLong}
AND (
(
p.latitude BETWEEN {$bounds->innerMinLat}
AND {$bounds->innerMaxLat}
AND p.longitude BETWEEN {$bounds->innerMinLong}
AND {$bounds->innerMaxLong}
)
OR (
6371 * ACOS(
COS(RADIANS({ $lat })) * COS(RADIANS(`latitude`)) * COS(
RADIANS(`longitude`) - RADIANS({ $lon })
) + SIN(RADIANS({ $lat })) * SIN(RADIANS(`latitude`))
)
)
) <= {$radius}
AND f.personal_id = {$personal_id}
ORDER BY distance ASC

重要

上面有可读性的文本,请确保这些值正确转义/最好参数化

假设您有正确的索引,这个查询应该保持快速并允许您进行连接。

看上面的代码,我不确定 personal_id 来自哪里,所以保持原样

如果需要查询的距离,可以去掉S1方 block

    (
p.latitude BETWEEN {$bounds->innerMinLat}
AND {$bounds->innerMaxLat}
AND p.longitude BETWEEN {$bounds->innerMinLong}
AND {$bounds->innerMaxLong}
)

并移动 OR

的第二部分
  6371 * ACOS(
COS(RADIANS({ $lat })) * COS(RADIANS(`latitude`)) * COS(
RADIANS(`longitude`) - RADIANS({ $lon })
) + SIN(RADIANS({ $lat })) * SIN(RADIANS(`latitude`))
)

回到 select,它仍然使用 S2。

我还要确保删除查询中的“魔数(Magic Number)” 6371 是地球的半径(以公里为单位)

关于php - Haversine 公式中的连接操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40199403/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com