0

我有一个 MySQL 例程,当通过使用 Haversin 方程传递纬度和经度时,它正在获取半径 50 英里内的记录。

虽然这很好用,而且速度非常快(考虑到它正在搜索 82k 记录),但我认为我可以通过使用 POINT 列创建类似的过程来获得更好的性能。

因此,在我的表中,我创建了一个名为 的额外列Location,给它一个 POINT 数据类型,更新了我的数据以将 lat & lon 传递给该Location列。数据有效,没问题,并添加了一个Spatial Index

问题是,如何将以下查询转换为使用Location列,而不是latlon列。

SET @LAT := '37.953';
SET @LON := '-105.688';

SELECT DISTINCT
BPZ.`store_id`,         
3956 * 2 * ASIN(SQRT(POWER(SIN((@LAT - abs(Z.`lat`)) * pi()/180 / 2),2) + COS(@LAT * pi()/180 ) * COS(abs(Z.`lat`) *  pi()/180) * POWER(SIN((@LON - Z.`lon`) *  pi()/180 / 2), 2))) as distance,
c.`name`,c.`address`,c.`city`,c.`state`,c.`phone`,c.`zip`,c.`premise_type`
FROM
`zip_codes` as Z, 
`brand_product_zip` as BPZ
LEFT JOIN `customers` c ON c.`store_id` = BPZ.`store_id`
WHERE
BPZ.`zip` = Z.`zip`
AND 
3956 * 2 * ASIN(SQRT(POWER(SIN((@LAT - abs(Z.`lat`)) * pi()/180 / 2),2) + COS(@LAT * pi()/180 ) * COS(abs(Z.`lat`) *  pi()/180) * POWER(SIN((@LON - Z.`lon`) *  pi()/180 / 2), 2))) <= 50
ORDER BY
distance LIMIT 20

我知道以前有人问过这个问题,但是,我看到的所有内容都指向基于latlon不是POINT列的计算

更新代码:

SET @lat = 41.92;
SET @lon = -72.65;
SET @kmRange = 80.4672; -- = 50 Miles

SELECT *, (3956 * 2 * ASIN(SQRT(POWER(SIN((@lat - abs(`lat`)) * pi()/180 / 2),2) + COS(@lat * pi()/180 ) * COS(abs(`lat`) *  pi()/180) * POWER(SIN((lon - `lon`) *  pi()/180 / 2), 2)))) as distance
FROM    `zip_codes`
WHERE   MBRContains(LineString(Point(@lat + @kmRange / 111.1, @lon + @kmRange / (111.1 / COS(RADIANS(@lat)))), Point(@lat - @kmRange / 111.1, @lon - @kmRange / (111.1 / COS(RADIANS(@lat))))), `Location`)
Order By distance
LIMIT 20
4

2 回答 2

2

您是否研究过希尔伯特曲线解决方案?空间索引不能提供确切的解决方案?. 使用 mysql 空间索引,您可以使用 mbrcontains:

CREATE TABLE lastcrawl (id INT NOT NULL PRIMARY KEY, pnt POINT NOT NULL) ENGINE=MyISAM;

INSERT
INTO    lastcrawl
VALUES  (1, POINT(40, -100));

SET @lat = 40;
SET @lon = -100;

SELECT  *
FROM    lastcrawl
WHERE   MBRContains
                (
                LineString
                        (
                        Point
                                 (
                                 @lat + 10 / 111.1,
                                 @lon + 10 / ( 111.1 / COS(RADIANS(@lat)))
                                 ),
                        Point    (
                                 @lat - 10 / 111.1,
                                 @lon - 10 / ( 111.1 / COS(RADIANS(@lat)))
                                 )
                        ),
                pnt
                );

看这里:MySQL - 在空间点附近选择。这里:http ://www.drdobbs.com/database/space-filling-curves-in-geospatial-appli/184410998

于 2013-05-29T14:00:52.523 回答
1

MySQL 的最近位置查找器一文详细解释了各种选项,以及从 MySQL 5.6 开始与空间扩展一起使用的最佳选择。

从文章中,这个示例查询列出了从给定坐标(42.81,-70.81)半径 50 英里内的邮政编码:

SELECT zip, primary_city,
       latitude, longitude, distance_in_mi
  FROM (
SELECT zip, primary_city, latitude, longitude,r,
       69.0 * DEGREES(ACOS(COS(RADIANS(latpoint))
                 * COS(RADIANS(latitude))
                 * COS(RADIANS(longpoint) - RADIANS(longitude))
                 + SIN(RADIANS(latpoint))
                 * SIN(RADIANS(latitude)))) AS distance_in_mi
 FROM zip
 JOIN (
        SELECT  42.81  AS latpoint,  -70.81 AS longpoint, 50.0 AS r
   ) AS p
 WHERE latitude
  BETWEEN latpoint  - (r / 69)
      AND latpoint  + (r / 69)
   AND longitude
  BETWEEN longpoint - (r / (69 * COS(RADIANS(latpoint))))
      AND longpoint + (r / (69 * COS(RADIANS(latpoint))))
  ) d
 WHERE distance_in_mi <= r
 ORDER BY distance_in_mi;
于 2014-03-31T23:06:04.883 回答