문제

나는 현재 단지에 백만 위치에서 mysql 데이터베이스 모두와 함께 위도 경도 정보입니다.

내가 노력하고 있을 찾기 위해 거리로 하나의 지점과 다른 많은 포인트를 통해 쿼리를 수행합니다.그것은 빠르게 내가 원하는 특히 100+두 번째 명.

이 있는 빠른 쿼리 또는 아마도 더 빠른 시스템이 다른 것보다 mysql 이?내가 사용하는 이 쿼리:

SELECT 
  name, 
   ( 3959 * acos( cos( radians(42.290763) ) * cos( radians( locations.lat ) ) 
   * cos( radians(locations.lng) - radians(-71.35368)) + sin(radians(42.290763)) 
   * sin( radians(locations.lat)))) AS distance 
FROM locations 
WHERE active = 1 
HAVING distance < 10 
ORDER BY distance;

참고:제공하는 거리에 마일.해야 하는 경우 킬로미터, 용 63713959.

도움이 되었습니까?

해결책

  • 을 만들의 포인트 사용 Point 의 값 Geometry 데이터 형식 MyISAM 테이블. 로 Mysql5.7.5, InnoDB 테이블도 지원 SPATIAL 지 있다.

  • SPATIAL 이러한 점에 인덱스

  • MBRContains() 하는 값을 찾기:

    SELECT  *
    FROM    table
    WHERE   MBRContains(LineFromText(CONCAT(
            '('
            , @lon + 10 / ( 111.1 / cos(RADIANS(@lon)))
            , ' '
            , @lat + 10 / 111.1
            , ','
            , @lon - 10 / ( 111.1 / cos(RADIANS(@lat)))
            , ' '
            , @lat - 10 / 111.1 
            , ')' )
            ,mypoint)
    

, 나 MySQL 5.1 상:

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

이것은 선택의 모든 점 약 내 상자 (@lat +/- 10 km, @lon +/- 10km).

이것은 실제로 상자나,둥근 사각형:위도 경도밖에 없는 세그먼트의 구체입니다.이 다를 수 있습니다 일반에서 사각형 프란츠 요셉은 땅, 지만,그것을 아주 가까이에서 가장 살고 있는 곳이다.

  • 추가 필터링을 선택하려면 내부의 모든 것을 원하지 않는(사각형)

  • 적용 가능한 추가적인 정밀한 필터링하의 계정에 대한 큰 원거리(에 대한 큰 거리)

다른 팁

지 MySql 특정 응답이지만,그것의 성능을 향상시키기 위해 sql statement.

당신이 무엇을 효과적으로 하는 이의 거리를 계산하는 모든 지점에서,테이블의 경우를 참조하십시오에서는 10 마일 단위로 주어진 지점.

무엇을 할 수 있을 실행하기 전에 이 sql,이 만드는 점 그리기 상자 20 단위에서,당신의 점에서 중심가.e..(x1,y1)...(x4,y4),where(x1,y1)가(givenlong+10 단위,givenLat+10units)...(givenLong-10units,givenLat-10 단위).실제로,당신은 단지 두 가지 포인트,상단 왼쪽과 오른쪽 아래들(X1,Y1)과(X2,Y2)

지금 당신의 SQL 문을 사용하여 이러한 점을 제외하는 행실은 이상 10u 에서 주어진 지점,그것을 사용할 수 있는 인덱스에 위도&경도 그렇게 될 것입니다 몇 배 더 빠른 것보다 당신이 현재 있다.

예:

select . . . 
where locations.lat between X1 and X2 
and   locations.Long between y1 and y2;

상자에 접근할 수 있는 false 를 반환 긍정(선택할 수 있습니다 포인트의 모서리에있는 상자이>10u 에서 주어진 지점),그래서 당신은 여전히 필요로의 거리를 계산하기 위해 각 지점입니다.그러나 이것은 다시 될 것입니다 훨씬 빠르기 때문에 당신은 크게 제한된 수의 포인트를 테스트하는 내 포인트 상자입니다.

저는 이 기술은"생각 상자 안에":)

편집: 이 될 수 있습니에 넣고 하나 SQL 문을?

나는 아무 생각이 무엇 mySql Php 할 수 있습니다.어디 있는지 모르겠어 가장 좋은 장소입을 구축하는 네 개의 포인트 또는 그들이 어떻게 전달할 수 있습 mySql 쿼리 Php.그러나,일단 당신이 당신의 네 개의 포인트는,아무것도 당신을 막을 결합하여 자신의 SQL 문으로 광산이다.

select name, 
       ( 3959 * acos( cos( radians(42.290763) ) 
              * cos( radians( locations.lat ) ) 
              * cos( radians( locations.lng ) - radians(-71.35368) ) 
              + sin( radians(42.290763) ) 
              * sin( radians( locations.lat ) ) ) ) AS distance 
from locations 
where active = 1 
and locations.lat between X1 and X2 
and locations.Long between y1 and y2
having distance < 10 ORDER BY distance;

알 MS SQL 나를 구축할 수 있는 SQL 문을 선언하는 네 개의 수레(X1,Y1,X2,Y2)및 그들을 계산하기 전에"기본"선택한 문 제가 말한 것처럼,나는 아무 생각하는 경우 이 함께 할 수 있습니다.MySql.그러나 나는 여전히는 경향이 있을 구축하는 네 개의 포인트에서는 C#과 전달을 매개변수로서 SQL 쿼리가 있습니다.

죄송할 수 없 더 많은 도움이 될 경우,사람이 대답할 수 있 MySQL&Php 의 특정 부분에 이를 편집 이 대답니다.

이 프리젠테이션에 대한 좋은 대답합니다.기본적으로 표시하는 두 가지 방법이 다음과 같 의견,대한 자세한 설명과 함께서 왜/때 중 하나를 사용해야 합니다 또 다른 이유"상자에서"계산을 수 있습니다 매우 흥미 롭습니다.

지역 거리로 검색 MySQL

이 블로그 게시물, 다음 MySql 기능에 게시했습니다.나는 그것을 테스트하지 않았지만,무엇에서 나는 게시물 하는 경우 위도 경도 분야는 인덱스, 이 작동할 수 있지만 당신을 위해:

DELIMITER $$

DROP FUNCTION IF EXISTS `get_distance_in_miles_between_geo_locations` $$
CREATE FUNCTION get_distance_in_miles_between_geo_locations(geo1_latitude decimal(10,6), geo1_longitude decimal(10,6), geo2_latitude decimal(10,6), geo2_longitude decimal(10,6)) 
returns decimal(10,3) DETERMINISTIC
BEGIN
  return ((ACOS(SIN(geo1_latitude * PI() / 180) * SIN(geo2_latitude * PI() / 180) + COS(geo1_latitude * PI() / 180) * COS(geo2_latitude * PI() / 180) * COS((geo1_longitude - geo2_longitude) * PI() / 180)) * 180 / PI()) * 60 * 1.1515);
END $$

DELIMITER ;

샘플 사용: 가정이라는 테이블이 장소와 함께 필드를 위도 경도:

선택 get_distance_in_miles_between_geo_locations(-34.017330, 22.809500,위도,경도)로 distance_from_input 장소에서;

모든 집어에서 이 게시물

SELECT * FROM (SELECT *,(((acos(sin((43.6980168*pi()/180)) * 
sin((latitude*pi()/180))+cos((43.6980168*pi()/180)) * 
cos((latitude*pi()/180)) * cos(((7.266903899999988- longitude)* 
pi()/180))))*180/pi())*60*1.1515 ) as distance 
FROM wp_users WHERE 1 GROUP BY ID limit 0,10) as X 
ORDER BY ID DESC

이는 거리 계산 쿼리 사이를 점에서 MySQL,내가 그것을 사용에 데이터베이스에,그것은 그것을 완벽하게 작동하!참고:해당 변경사항(데이터베이스 이름,테이블 이름,열 등)에 따라 귀하의 요구 사항입니다.

set @latitude=53.754842;
set @longitude=-2.708077;
set @radius=20;

set @lng_min = @longitude - @radius/abs(cos(radians(@latitude))*69);
set @lng_max = @longitude + @radius/abs(cos(radians(@latitude))*69);
set @lat_min = @latitude - (@radius/69);
set @lat_max = @latitude + (@radius/69);

SELECT * FROM postcode
WHERE (longitude BETWEEN @lng_min AND @lng_max)
AND (latitude BETWEEN @lat_min and @lat_max);

를 사용하는 경우 MySQL5.7.*, 다음 사용할 수 있습니다 st_distance_sphere(포인트,점).

Select st_distance_sphere(POINT(-2.997065, 53.404146 ), POINT(58.615349, 23.56676 ))/1000  as distcance
   select
   (((acos(sin(('$latitude'*pi()/180)) * sin((`lat`*pi()/180))+cos(('$latitude'*pi()/180)) 
    * cos((`lat`*pi()/180)) * cos((('$longitude'- `lng`)*pi()/180))))*180/pi())*60*1.1515) 
    AS distance
    from table having distance<22;

전체 코드에 대한 정보를 설치하는 방법으로 MySQL 플러그인은 여기: https://github.com/lucasepe/lib_mysqludf_haversine

게시 이 지난해 같다.때문에 친절하게@TylerCollier 날 제안 게시물에 대한 답변으로,여기 있습니다.

다른 방법은 사용하는 UDF 를 반환하는 함수의 하버 사인 거리에서 두 개의 포인트입니다.이 함수에 입력:

lat1 (real), lng1 (real), lat2 (real), lng2 (real), type (string - optinal - 'km', 'ft', 'mi')

그래서 우리는 뭔가를 쓸 수 있습니다 다음과 같다:

SELECT id, name FROM MY_PLACES WHERE haversine_distance(lat1, lng1, lat2, lng2) < 40;

모든 가 기록 거리에 적은 다음 40 킬로미터입니다.또:

SELECT id, name FROM MY_PLACES WHERE haversine_distance(lat1, lng1, lat2, lng2, 'ft') < 25;

모든 가 기록 거리에 적은 다음 25 일 발입니다.

핵심 기능입니다:

double
haversine_distance( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ) {
    double result = *(double*) initid->ptr;
    /*Earth Radius in Kilometers.*/ 
    double R = 6372.797560856;
    double DEG_TO_RAD = M_PI/180.0;
    double RAD_TO_DEG = 180.0/M_PI;
    double lat1 = *(double*) args->args[0];
    double lon1 = *(double*) args->args[1];
    double lat2 = *(double*) args->args[2];
    double lon2 = *(double*) args->args[3];
    double dlon = (lon2 - lon1) * DEG_TO_RAD;
    double dlat = (lat2 - lat1) * DEG_TO_RAD;
    double a = pow(sin(dlat * 0.5),2) + 
        cos(lat1*DEG_TO_RAD) * cos(lat2*DEG_TO_RAD) * pow(sin(dlon * 0.5),2);
    double c = 2.0 * atan2(sqrt(a), sqrt(1-a));
    result = ( R * c );
    /*
     * If we have a 5th distance type argument...
     */
    if (args->arg_count == 5) {
        str_to_lowercase(args->args[4]);
        if (strcmp(args->args[4], "ft") == 0) result *= 3280.8399;
        if (strcmp(args->args[4], "mi") == 0) result *= 0.621371192;
    }

    return result;
}

빠르고 간단하고 정확하(을 위한 작은 거리)근사를 수행할 수 있습으로 구형 프로젝션.적어도 내 routing 알고리즘을 얻을 20%향상에 비해 정확한 계산이 됩니다.자바 코드에서 그것은 좋아하는 것을 봅니다:

public double approxDistKm(double fromLat, double fromLon, double toLat, double toLon) {
    double dLat = Math.toRadians(toLat - fromLat);
    double dLon = Math.toRadians(toLon - fromLon);
    double tmp = Math.cos(Math.toRadians((fromLat + toLat) / 2)) * dLon;
    double d = dLat * dLat + tmp * tmp;
    return R * Math.sqrt(d);
}

에 대한 확실하지 않 MySQL(니다!).

해야 당신이 알고에 대한 제한 사항(제 param 의 assertEquals 의미에서의 정확성 킬로미터):

    float lat = 24.235f;
    float lon = 47.234f;
    CalcDistance dist = new CalcDistance();
    double res = 15.051;
    assertEquals(res, dist.calcDistKm(lat, lon, lat - 0.1, lon + 0.1), 1e-3);
    assertEquals(res, dist.approxDistKm(lat, lon, lat - 0.1, lon + 0.1), 1e-3);

    res = 150.748;
    assertEquals(res, dist.calcDistKm(lat, lon, lat - 1, lon + 1), 1e-3);
    assertEquals(res, dist.approxDistKm(lat, lon, lat - 1, lon + 1), 1e-2);

    res = 1527.919;
    assertEquals(res, dist.calcDistKm(lat, lon, lat - 10, lon + 10), 1e-3);
    assertEquals(res, dist.approxDistKm(lat, lon, lat - 10, lon + 10), 10);

여기에 매우 상세한 설명의 지리적 거리를 검색 MySQL 을 기준으로 솔루션을 구현 하버 사인 수식 mysql.완벽한 솔루션 설명과 이론,구현 및한 성능 최적화입니다.지만 공간 최적화 부분에 작동하지 않았정을 수도 있습니다.http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL

의 읽기 지역 거리로 검색 MySQL, 솔루션 기반으로 구현 하버 사인 수식 MySQL.이것은 완벽한 솔루션 설명과 이론,구현 및한 성능 최적화입니다.지만 공간 최적화는 일부가 제대로 동작하지 않을 수도 있습니다.

나는 두 가지 실수를 이:

  1. 의 사용 abs 에서 선택에 문 p8.나는 그냥 생략 abs 고 일했다.

  2. 공간 검색 거리의 기능에 p27 으로 변환하지 않습니다 라디안 또는 곱하기 경도에 의해 cos(latitude), 지 않는 한,자신의 공간 데이터를 로드와이에서 고려사항(말할 수 없습에서의 컨텍스트 문서),하지만 그의 예에 p26 을 나타내는 그의 공간 데이터 POINT 지 않은로드와 라디안 또는 도입니다.

MySQL 기능의 수를 반환합 시 사이에는 두 개의 좌표:

CREATE FUNCTION DISTANCE_BETWEEN (lat1 DOUBLE, lon1 DOUBLE, lat2 DOUBLE, lon2 DOUBLE)
RETURNS DOUBLE DETERMINISTIC
RETURN ACOS( SIN(lat1*PI()/180)*SIN(lat2*PI()/180) + COS(lat1*PI()/180)*COS(lat2*PI()/180)*COS(lon2*PI()/180-lon1*PI()/180) ) * 6371000

를 반환하는 값에는 다른 형식으로 대체 6371000 함수에서의 반경을 지구에서 당신의 선택의 단위입니다.예를 들어,킬로미터 것 6371 마일 것 3959.

는 기능을 사용하려면 단지,그것은 당신이 어떤 다른 함수에서 MySQL.예를 들어,당신은 테이블 city, 할 수 있습을 찾아 사이의 거리가 모든 도시의 모든 다른 도시:

SELECT
    `city1`.`name`,
    `city2`.`name`,
    ROUND(DISTANCE_BETWEEN(`city1`.`latitude`, `city1`.`longitude`, `city2`.`latitude`, `city2`.`longitude`)) AS `distance`
FROM
    `city` AS `city1`
JOIN
    `city` AS `city2`

I 를 해결하기 위해 필요한 유사한 문제(필터링하여 행 거리에서 단일 지점)과 결합하여 원래의 질문에 답변 및 코멘트,내가 가진 솔루션을 완벽하게 작동에 나를 위해 모두 MySQL5.6 5.7.

SELECT 
    *,
    (6371 * ACOS(COS(RADIANS(56.946285)) * COS(RADIANS(Y(coordinates))) 
    * COS(RADIANS(X(coordinates)) - RADIANS(24.105078)) + SIN(RADIANS(56.946285))
    * SIN(RADIANS(Y(coordinates))))) AS distance
FROM places
WHERE MBRContains
    (
    LineString
        (
        Point (
            24.105078 + 15 / (111.320 * COS(RADIANS(56.946285))),
            56.946285 + 15 / 111.133
        ),
        Point (
            24.105078 - 15 / (111.320 * COS(RADIANS(56.946285))),
            56.946285 - 15 / 111.133
        )
    ),
    coordinates
    )
HAVING distance < 15
ORDER By distance

coordinates 이것은 유형 POINTSPATIAL index
6371 이 계산을 위한 킬로미터 거리
56.946285 은 위도에 대한 중앙 포인트
24.105078 은 경도 중앙점
10 는 최대 거리에서 킬로미터

내에서 테스트,MySQL 사용하여 공간 인덱스 coordinates 필드를 빠르게 선택하는 모든 행 내에있는 사각형 및 그 실제 거리를 계산을 위한 모든 필터링의 장소를 제외 장소에서 사각형 모서리를 떠나는 유일한 장소 안쪽 원입니다.

이것은 시각화의 결과는:

map

회색 stars 시각화의 모든 점도,노란색 별이 반환된 것 MySQL 쿼리가 있습니다.회색 성 내부의 모서리의 직사각형(그러나 외부 원)에 의해 선택되었다 MBRContains() 고 다음을 선택 해제하여 HAVING 절입니다.

를 사용하여 mysql

SET @orig_lon = 1.027125;
SET @dest_lon = 1.027125;

SET @orig_lat = 2.398441;
SET @dest_lat = 2.398441;

SET @kmormiles = 6371;-- for distance in miles set to : 3956

SELECT @kmormiles * ACOS(LEAST(COS(RADIANS(@orig_lat)) * 
 COS(RADIANS(@dest_lat)) * COS(RADIANS(@orig_lon - @dest_lon)) + 
 SIN(RADIANS(@orig_lat)) * SIN(RADIANS(@dest_lat)),1.0)) as distance;

보: https://andrew.hedges.name/experiments/haversine/

보: https://stackoverflow.com/a/24372831/5155484

보: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/

참고: LEAST 을 방지하기 위해 사용됩니다 null 값으로 주석 제안에 https://stackoverflow.com/a/24372831/5155484

$objectQuery = "SELECT table_master.*, ((acos(sin((" . $latitude . "*pi()/180)) * sin((`latitude`*pi()/180))+cos((" . $latitude . "*pi()/180)) * cos((`latitude`*pi()/180)) * cos(((" . $longitude . "- `longtude`)* pi()/180))))*180/pi())*60*1.1515  as distance FROM `table_post_broadcasts` JOIN table_master ON table_post_broadcasts.master_id = table_master.id WHERE table_master.type_of_post ='type' HAVING distance <='" . $Radius . "' ORDER BY distance asc";
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top