选择距离为5km或更大的记录

时间:2015-06-29 10:41:23

标签: mysql

我正在开发一个位置应用程序,我需要从我的Mysql Location表中获取所有位置,它们之间的距离为5km。

对于Ex位置表,下面有条目:

  id     Latitude       Longitude
  1     22.7499180     75.8950577
  2     22.7498474     75.8950653
  3     22.7498035     75.8950424
  4     22.7497787     75.8950729
  5     22.7498245     75.8950806
  6     22.7497902     75.8950272
  7     22.7497864     75.8950424
  8     22.7497768     75.8950500
  9     22.7497864     75.8950577
  10    22.7497921     75.8950653
  11    22.7497597     75.8950653
  12    22.7498283     75.8950653
  13    22.7497978     75.8950577

所以从上面的表格中我需要获取类似这样的结果

  id     Latitude       Longitude   Distance (>=5Km)
  1     22.7499180     75.8950577     --
  4     22.7497787     75.8950729    6km (From lat long of id 1)
  8     22.7497768     75.8950500    8km (From lat long of id 4)
  11    22.7497597     75.8950653    6km (From lat long of id 8)
  13    22.7497978     75.8950577    10km (From lat long of id 11)

我搜索了很多以获得这样的结果,但我只是根据一些固定的纬度/长度或固定半径得到结果。如果可能,请帮助Mysql查询。

修改(来自OP的评论)

我需要的是计算距离上一个选定值的距离......对于Ex。从记录1开始。将距离1与记录2进行比较,它是<1。 5km,与记录3相比也<5。 5km,与4相比,距离> 5km。 5km所以我们将它保持在列表中,而下一个记录将与记录4进行比较。因此4的距离将与5进行比较,如果记录5的距离> 5。距离4下一个比较5km,记录5作为参考。

3 个答案:

答案 0 :(得分:6)

没有存储过程,只是纯粹的肆无忌惮的sql荣耀:

SET @prevLong=-1.0000;
SET @prevLat=-1.0000;
SET @currDist=1.0000;
select id, diff from (
select id,
@prevLat prev_lat,
@currDist:= 6371 * 2 * (atan2(sqrt(sin(radians(@prevLat - lat)/2)
                       * sin(radians(@prevLat - lat)/2)
                       + cos(radians(lat))
                       * cos(radians(@prevLat))
                       * sin(radians(@prevLong - longi)/2)
                       * sin(radians(@prevLong - longi)/2))
                  ,sqrt(1-(sin(radians(@prevLat - lat)/2)
                           * sin(radians(@prevLat - lat)/2)
                           + cos(radians(lat))
                           * cos(radians(@prevLat))
                           * sin(radians(@prevLong
                                         - longi)/2)
                           * sin(radians(@prevLong - longi)/2))))) diff,
@prevLong prevLong,
case when @currdist > 5 then @prevLat:=lat  else null end  curr_lat,
case when @currDist > 5 then @prevLong:= longi  else null end  curr_long

  from latLong
order by id asc
) a where diff > 5

SQLFiddle证明魔法是真实的: http://sqlfiddle.com/#!9/7e4fe/19

修改 在Codeigniter中,您可以使用如下变量:

$this->db->query("SET @prevLong=-1.0000");
$this->db->query("SET @prevLat=-1.0000");
$this->db->query("SET @prevDist=-1.0000");

然后正常发出您的查询

$query= $this->db->query("SELECT ...");

答案 1 :(得分:2)

所以你需要计算Lat Lon的距离,然后检查结果是否大于5km。您的样本数据存在问题,即计算的距离在范围内,因此您无法获得任何结果。我想你还有几个地方可以在你的桌子上查看。

SELECT 
    a.id, a.Latitude, a.Longitude, CONCAT(a.ID,"-",b.ID) as 'FromTo',
    6371 * acos( 
                  cos(radians( b.Latitude ))
                * cos(radians( a.Latitude ))
                * cos(radians( b.Longitude ) - radians( a.Longitude ))
                + sin(radians( b.Latitude )) 
                * sin(radians( a.Latitude ))) as distance 
FROM new_table a INNER JOIN new_table b ON a.id <> b.id
HAVING distance >= 0.001
ORDER BY id, distance;

我将having子句设置为大于一米HAVING distance >= 0.001。如果你想检查公里相应调整!

修改

这可能不是你可能需要稍微调整一下的解决方案,但程序看起来像

DELIMITER $$
CREATE PROCEDURE `calcDistWithin`(IN dist double)
BEGIN

declare maxTempID int;
declare maxTblID int;
declare breakLoop boolean;
SET breakLoop = FALSE;

DROP TEMPORARY TABLE IF EXISTS tmp;
CREATE TEMPORARY TABLE tmp (ID int, Latitude double, Longitude double, distance double, toID varchar(10));
DROP TEMPORARY TABLE IF EXISTS tmpOUT;
CREATE TEMPORARY TABLE tmpOUT (ID int, Latitude double, Longitude double, distance double, toID varchar(10));

INSERT INTO tmp select ID, Latitude, Longitude, 0, "---"  from new_table limit 1;
INSERT INTO tmpOUT select ID, Latitude, Longitude, 0, "---"  from new_table limit 1;

SELECT ID INTO maxTblID FROM new_table ORDER BY ID DESC LIMIT 1;
SELECT ID into maxTempID FROM tmp ORDER BY ID DESC LIMIT 1;

WHILE breakLoop = FALSE DO

    IF EXISTS (SELECT
                6371 * acos( 
                  cos(radians( b.Latitude ))
                * cos(radians( a.Latitude ))
                * cos(radians( b.Longitude ) - radians( a.Longitude ))
                + sin(radians( b.Latitude )) 
                * sin(radians( a.Latitude ))) as distance
               FROM tmp a INNER JOIN new_table b 
               WHERE a.ID < b.ID AND a.ID = maxTempID
               HAVING distance >= dist
               LIMIT 1) THEN

        INSERT INTO tmpOUT SELECT
            b.ID, b.Latitude, b.Longitude,
            6371 * acos( 
                  cos(radians( b.Latitude ))
                * cos(radians( a.Latitude ))
                * cos(radians( b.Longitude ) - radians( a.Longitude ))
                + sin(radians( b.Latitude )) 
                * sin(radians( a.Latitude ))) as distance,
            a.ID
        FROM tmp a INNER JOIN new_table b 
        WHERE a.ID < b.ID AND a.ID = maxTempID
        HAVING distance >= dist
        ORDER BY a.ID, b.ID, distance
        LIMIT 1;

        INSERT INTO tmp SELECT ID, Latitude, Longitude, distance, toID FROM tmpOUT ORDER BY ID DESC LIMIT 1;
        SELECT ID into maxTempID FROM tmpOUT order by ID DESC LIMIT 1;

    ELSE

        SET breakLoop = TRUE;

    END IF;

END WHILE;

SELECT * FROM tmpOUT;

END$$
DELIMITER ;

调用它只需使用

CALL calcDistWithin(5.00)

答案 2 :(得分:0)

我不确定你是否想要一个纯粹的SQL解决方案。 我只能用其他语言提供解决方案。 我假设使用id来确定最近的节点。

 List locat= new List(); 
  last = getTheLastRecord();
  locat.add(last);
  count = getTheCountOfRecord();
  for(int i=1;i<count;i++){//i = 1 because last record already read.
   Record r = getRecord(i);
   if(compareDistance(r,last)>5000){
     locat.add(r);
     last = r;
   }
 }

//最后,locat列表将包含您想要的每个位置。