【问题标题】:PHP&MySQL Store Distance Calculator - PostcodesPHP MySQL 存储距离计算器 - 邮政编码
【发布时间】:2011-08-13 01:02:25
【问题描述】:

是的,我一直在尝试研究如何将给定的邮政编码与说商店地址的数据库进行比较,并根据哪个最接近给定的邮政编码(或者我猜是邮政编码)对它们进行排序。

这主要是出于兴趣,而不是我向您征求意见,然后将其出售给客户:-O

首先,经过研究,我发现您必须使用 Lat/Long 进行距离,所以我找到了一个将邮政编码/邮政编码转换为 lat long 的 API,现在我的数据库具有 id, store_name, lat, long, postcode 之类的结构,我可以转换一个给定的邮政编码到一个纬度。

但是如何在 SQL 中查询最接近给定纬度的那些?

【问题讨论】:

    标签: php mysql sql distance


    【解决方案1】:

    试试这样的:

    // get all the zipcodes within the specified radius - default 20
        function zipcodeRadius($lat, $lon, $radius)
        {
            $radius = $radius ? $radius : 20;
            $sql = 'SELECT distinct(ZipCode) FROM zipcode  WHERE (3958*3.1415926*sqrt((Latitude-'.$lat.')*(Latitude-'.$lat.') + cos(Latitude/57.29578)*cos('.$lat.'/57.29578)*(Longitude-'.$lon.')*(Longitude-'.$lon.'))/180) <= '.$radius.';';
            $result = $this->db->query($sql);
            // get each result
            $zipcodeList = array();
            while($row = $this->db->fetch_array($result))
            {
                array_push($zipcodeList, $row['ZipCode']);
            }
            return $zipcodeList;
        }
    

    更新: 有一些关于效率的讨论。下面是这个查询的一个小基准。我有一个包含美国每个邮政编码的数据库。其中一些是重复的,因为邮政编码的工作方式(超出了本主题的范围)。所以我只有不到 8 万条记录。我在 90210 上跑了 20 英里半径的距离:

    SELECT distinct(ZipCode) FROM zipcodes  WHERE (3958*3.1415926*sqrt((Latitude-34.09663010)*(Latitude-34.09663010) + cos(Latitude/57.29578)*cos(34.09663010/57.29578)*(Longitude- -118.41242981)*(Longitude- -118.41242981))/180) <= 20
    

    我返回了 366 条记录,查询耗时 0.1770 秒。您需要提高多少效率?

    【讨论】:

    • 它返回特定半径内的邮政编码(本例中为 20)。您可以使用以下方法计算 2 个坐标之间的距离:6371*acos(cos(LatitudeA)*cos(LatitudeB)*cos(longitudeB -longitudeA)+sin(LatitudeA)*sin(latitudeB)),坐标为弧度
    • 它为数据库中的所有点计算它们是否落入一个圆圈(因此使用PI、sqrt和cos)然后选择属于这些点的邮政编码。
    • 效率不是很高,因为它会计算数据库中每个条目的距离。最好先创建一个边界框,然后使用 WHERE 子句根据边界框的纬度/经度进行选择,然后只对边界框内的行子集执行公式。
    • 抱歉,我得看看这方面的基准。 MySQL 可以通过这种计算处理 43k 条记录,因此不必担心。我有一个包含 80k 条记录的数据库(覆盖美国全境),耗时 0.2630 秒。您还能节省多少?
    【解决方案2】:

    请参阅this answer to a previous question 以获取在查询 MySQL 之前计算边界框的示例。这允许 MySQL 查询中的复杂公式针对数据库条目的子集运行,而不是针对表中的每个条目。

    【讨论】:

    • 美国大约有 43,000 个邮政编码。我敢说,做这样的事情的所有额外代码都是矫枉过正的。虽然这是一堂关于优化的重要课程,但我不确定它是否完全有必要,是吗?
    • 所有额外的代码?构建边界框的开销很小。在执行距离计算之前,使用边界框将数据库中的 1,000 条记录过滤到 10 条记录的减少是巨大的......即使使用边界框的第一遍不返回任何记录,所以边界框需要进行六分法两到三倍,它仍然比计算每一行的距离快得多,即使在小数据集上也是如此
    • 你能在这方面展示一些基准吗?我在评论中展示了查询美国的每个邮政编码需要几分之一秒的时间。您需要提高多少效率?
    • 不,我不能引用任何基准......而且我目前无法执行任何测试,所以我想这证明我错了,因为我无法证明我是对的;但是使用 WHERE 子句将计算限制为(例如)10 行(使用 WHERE 子句针对索引选择)而不是 1000 行通常会对 SQL 查询产生一些影响。
    • 你是对的!我确信它会有所作为。但是,在这么少的记录集上,人类不会注意到差异。
    【解决方案3】:

    check out this great open source project

    免责声明:不是我的项目,我也不是贡献者。纯属推荐。

    【讨论】:

    • 天哪,我的名字在 :-)
    • 哦,是的,我会说命运(如果我相信那种东西的话)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-25
    • 2014-06-09
    • 1970-01-01
    • 2011-04-01
    • 2011-04-28
    相关资源
    最近更新 更多