在postgresql中找到纬度和经度的最近位置

时间:2016-06-15 06:17:49

标签: sql postgresql spatial

您好我在postgresql数据库中尝试通过纬度和经度查找最近的位置。但是当我运行以下查询时,它显示列距离不存在。

ERROR:  column "distance" does not exist
LINE 1: ... ) ) ) AS distance FROM station_location   HAVING distance <...
                                                             ^
********** Error **********

ERROR: column "distance" does not exist
SQL state: 42703
Character: 218

CREATE TABLE station_location
(
  id bigint NOT NULL DEFAULT nextval('location_id_seq'::regclass),
  state_name character varying NOT NULL,
  country_name character varying NOT NULL,
  locality character varying NOT NULL,
  created_date timestamp without time zone NOT NULL,
  is_delete boolean NOT NULL DEFAULT false,
  lat double precision,
  lng double precision,
  CONSTRAINT location_pkey PRIMARY KEY (id)
)

SELECT  *,( 3959 * acos( cos( radians(6.414478) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(12.466646) ) + sin( radians(6.414478) ) * sin( radians( lat ) ) ) ) AS distance 
FROM station_location
HAVING distance < 5
ORDER BY distance
LIMIT 20;

5 个答案:

答案 0 :(得分:6)

PostGIS的

不要将lat和long存储在这样的桌子上。而是使用PostGIS几何体或geography type

CREATE EXTENSION postgis;

CREATE TABLE foo (
  geog geography;
);

CREATE INDEX ON foo USING gist(geog);

INSERT INTO foo (geog)
  VALUES (ST_MakePoint(x,y));

现在,当您需要查询它时,您可以使用KNN (<->)来实际对索引执行此操作。

SELECT *
FROM foo
ORDER BY foo.geog <=> ST_MakePoint(x,y)::geog;

在您的查询中,您明确拥有HAVING distance < 5。你也可以在索引上做到这一点。

SELECT *
FROM foo
WHERE ST_DWithin(foo.geog, ST_MakePoint(x,y)::geography, distance_in_meters)
ORDER BY foo.geog <=> ST_MakePoint(x,y)::geog;

如果所有点都位于distance_in_meters之外,则确保不返回任何内容。

答案 1 :(得分:3)

select * from (
SELECT  *,( 3959 * acos( cos( radians(6.414478) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(12.466646) ) + sin( radians(6.414478) ) * sin( radians( lat ) ) ) ) AS distance 
FROM station_location
) al
where distance < 5
ORDER BY distance
LIMIT 20;

答案 2 :(得分:1)

请参阅this gist,您将了解如何在point类型上声明DOMAIN以及如何覆盖distance运算符以返回顺向距离。

声明从latlong继承的point类型:

CREATE DOMAIN latlong AS point CHECK (VALUE[0] BETWEEN -90.0 AND 90.0 AND VALUE[1] BETWEEN -180 AND 180);

以公里为单位的顺向距离(球体与地球半径之间的距离):

CREATE OR REPLACE FUNCTION orthodromic_distance(latlong, latlong) RETURNS float AS $_$
     SELECT acos(
              sin(radians($1[0])) 
            * 
              sin(radians($2[0]))
            + 
              cos(radians($1[0])) 
            * 
              cos(radians($2[0]))
            * 
              cos(radians($2[1]) 
            - 
              radians($1[1]))
            ) * 6370.0;
$_$ LANGUAGE sql IMMUTABLE;

当与latlongs一起使用时,使用此函数覆盖距离运算符<->

CREATE OPERATOR <-> ( PROCEDURE = orthodromic_distance
, LEFTARG = latlong, RIGHTARG = latlong
);

现在在SQL查询中,找到最近的实体:

WITH
  station_distance AS (
    SELECT
      id AS station_id,
      point(lat, long)::latlong <-> point(6.414478, 12.466646)::latlong AS distance
    FROM station_location
    WHERE NOT is_deleted
  )
  SELECT
    sl.state_name,
    sl.country_name,
    sl.locality,
    point(sl.lat, sl.long)::latlong AS coordinates,
    sd.distance
  FROM
    station_location sl
    JOIN station_distance sd
      ON sd.station_id = sl.id
  ORDER BY
    distance ASC
  LIMIT 10

您可能希望使用latlong类型将位置latlong存储在同一字段中。

答案 3 :(得分:1)

您可以使用PostgreSQL的cubeearthdistance扩展名。

像这样启用它们:

CREATE EXTENSION cube;
CREATE EXTENSION earthdistance;

假设您当前的位置是35.697933, 139.707318。然后您的查询将是这样的:

SELECT *, point(35.697933, 139.707318) <@>  (point(longitude, latitude)::point) as distance
FROM station_location
-- WHERE (point(35.697933, 139.707318) <@> point(longitude, latitude)) < 3
ORDER BY distance;

请注意,distance以英里为单位(默认情况下)。

答案 4 :(得分:0)

手册阐明了:

  

输出列的名称可以用来引用列中的值   ORDER BY和GROUP BY子句,但不在WHERE或HAVING子句中;   在那里,您必须写出表达式。

相关问题