Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/57.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
MySQL计算Zipcode之间的距离更快更准确?_Mysql_Sql - Fatal编程技术网

MySQL计算Zipcode之间的距离更快更准确?

MySQL计算Zipcode之间的距离更快更准确?,mysql,sql,Mysql,Sql,我有一个包含42000多个Zipcode、经度、纬度和州信息的表。使用输入的zipcode半径为25英里的所有zipcode返回结果的最准确和最快的查询是什么 当前代码(我认为不准确) 选择 zipcode( 3959*acos( cos(弧度(78.3232)) *cos(弧度(纬度)) *cos(弧度(经度)-弧度(65.3234)) +正弦(弧度(78.3232)) *sin(弧度(纬度)) ) )作为距离 从地点 距离小于25的 按距离排序 关于准确性 精确计算距离的唯一方法是使用3D

我有一个包含42000多个Zipcode、经度、纬度和州信息的表。使用输入的zipcode半径为25英里的所有zipcode返回结果的最准确和最快的查询是什么

当前代码(我认为不准确)

选择
zipcode(
3959*acos(
cos(弧度(78.3232))
*cos(弧度(纬度))
*cos(弧度(经度)-弧度(65.3234))
+正弦(弧度(78.3232))
*sin(弧度(纬度))
)
)作为距离
从地点
距离小于25的
按距离排序

关于准确性

精确计算距离的唯一方法是使用3D trig,就像您正在做的那样。您可以在此处阅读有关该主题的更多内容:

虽然zipcodes的lat/lng中心点之间的距离非常精确,这些中心点是任意选取的,并且距离是“像乌鸦一样”计算出来的,因此无法准确表示每个点内两个点之间的实际移动距离

例如,在相邻的zipcode中可能有两个相邻的住宅,或者在每个zipcode的相对端上有两个住宅,根据此计算,这两个住宅将计算为等距

纠正这个问题的唯一方法是计算地址距离,这需要USPS数据将地址映射到更具体的点,或者使用像Google Maps这样的API,这也将计算给定可用道路的实际行驶距离

在性能方面

有几种方法可以加快查询速度

1。减少实时数学运算量

实时计算的最快方法是预先计算并将昂贵的trig值存储在表中的列中,例如:

ALTER TABLE Location
    ADD COLUMN cos_rad_lat DOUBLE,
    ADD COLUMN cos_rad_lng DOUBLE,
    ADD COLUMN sin_rad_lat DOUBLE;
然后

在查询之外进行cos(弧度(78.3232))类型的计算,这样就不会对每一行数据进行计算

因此,将所有计算减少为常量值(在使用SQL之前)和计算列将使您的查询如下所示:

SELECT
    zipcode,
    3959 * acos(
        0.20239077538110228
        * cos_rad_lat
        * cos_rad_lng - 1.140108408597264
    )
    + 0.979304842243025 * sin_rad_lat AS distance
FROM Location
HAVING distance < 25
ORDER BY distance
你可以

FROM (
    SELECT * 
    FROM Location 
    WHERE latitude BETWEEN A and B
        AND longitude BETWEEN C and D
) AS Location
其中A、B、C和D是对应于您的中心点+-约0.3的数字(在美国,每10度lat/lng对应约5-7英里)

这种方法在-180/180经度时会变得棘手,但这不会影响美国

3。存储所有计算的距离 您可以做的另一件事是预先计算所有拉链的所有距离,然后将其存储在单独的表中

CREATE TABLE LocationDistance (
    zipcode1 varchar(5) NOT NULL REFERENCES Location(zipcode),
    zipcode2 varchar(5) NOT NULL REFERENCES Location(zipcode)
    distance double NOT NULL,
    PRIMARY KEY (zipcode1, zipcode2),
    INDEX (zipcode1, distance)
);
使用zip及其计算距离的每个组合填充此表

您的查询将如下所示:

SELECT zipcode2
FROM LocationDistance 
WHERE zipcode1 = 12345
    AND distance < 25;
选择zipcode2
从位置到距离
其中zipcode1=12345
距离<25;

这将是迄今为止最快的解决方案,尽管它需要存储10亿条记录。

看起来您已经知道如何使用
Latitud、Logitud

最快的方法是在
ZIPCODE

| X-25, Y-25 |            | X+25, Y-25 |        

                 X , Y

| X-25, Y+25 |            | X+25, Y+25 |        
因此,创建4个变量

Xleft = X - 25miles
Xright = X + 25miles
Ytop = Y - 25miles
Ybottom = Y + 25miles
如果latitud和longitud都有索引,那么这个查询几乎是即时的

SELECT *
FROM
  Location
WHERE 
    latitud between Xleft AND Xright
AND longitud between Ytop AND Ybottom


使用正方形会出现一些错误,但会过滤掉大部分错误的Zipcode。然后,您可以使用更小的数据集进行原始查询。

这可能是最快的,也可能不是最快的,但您可以通过首先预计算每个坐标对的法向量(NV),并根据向量的X、Y和Z分量来表示向量:

NV = [Nx, Ny, Nz]
在哪里

然后,可通过确定两个法向量NV1和NV2的差,并使用三维毕达哥拉斯方程计算任意两个坐标之间的距离,以获得两点之间的直线距离,即弦长C:

C = SQRT(dx^2+dy^2+dz^2)
在哪里

然后,可以通过以下公式求出大圆距离:

D = arcsin(C/2)*2*R
其中R是球体的半径,在这种情况下是地球,即3959英里

总而言之:

 select pt2.zip
      , asin(power(power(pt1.nx-pt2.nx,2)
                  +power(pt1.ny-pt2.ny,2)
                  +power(pt1.nz-pt2.nz,2)
            ,.5)/2)*2*3959 distance
   from (select 78.3232 lattitude
              , 65.3234 longitude
              , cos(radians(78.3232))*cos(radians(65.3234)) nx
              , cos(radians(78.3232))*sin(radians(65.3234)) ny
              , sin(radians(78.3232)) nz
        ) pt1
      , (select zip
              , lattitude
              , longitude
              , cos(radians(latitude))*cos(radians(longitude)) nx
              , cos(radians(latitude))*sin(radians(longitude)) ny
              , sin(radians(latitude)) nz
           from location) pt2
having distance < 25;

@你的常识我不会删除
sql
标记。因为是泛型标记。有了这个标签,只有寻找mysql的人才会看到这个问题。你当前的代码出了什么问题?这似乎不准确,无法判断正确的程度?距离错了吗?
78.3232-65.3234
是中心邮政编码,不是吗?您可以根据需要使用存储表方法-如果您愿意的话,这是一种机会。很可能你最终会使用所有10亿个组合:)第一个解决方案很好,但为这些字段添加
索引并不能提高性能。如果只计算距离(zip1,zip2)
其中zip1Nx = cos(radians(latitude))*cos(radians(longitude)) Ny = cos(radians(latitude))*sin(radians(longitude)) Nz = sin(radians(latitude))
C = SQRT(dx^2+dy^2+dz^2)
dx = Nx1-Nx2
dy = Ny1-Ny2
dz = Nz1-Nz2
D = arcsin(C/2)*2*R
 select pt2.zip
      , asin(power(power(pt1.nx-pt2.nx,2)
                  +power(pt1.ny-pt2.ny,2)
                  +power(pt1.nz-pt2.nz,2)
            ,.5)/2)*2*3959 distance
   from (select 78.3232 lattitude
              , 65.3234 longitude
              , cos(radians(78.3232))*cos(radians(65.3234)) nx
              , cos(radians(78.3232))*sin(radians(65.3234)) ny
              , sin(radians(78.3232)) nz
        ) pt1
      , (select zip
              , lattitude
              , longitude
              , cos(radians(latitude))*cos(radians(longitude)) nx
              , cos(radians(latitude))*sin(radians(longitude)) ny
              , sin(radians(latitude)) nz
           from location) pt2
having distance < 25;
 where pt2.latitude  between pt1.latitude - 25/69
                         and pt1.latitude + 25/69
   and pt2.longitude between pt1.longitude - 25/(69*cos(radians(abs(pt1.latitude)+25/69)))
                         and pt1.longitude + 25/(69*cos(radians(abs(pt1.latitude)+25/69)))