Reputation: 3454
I have two tables projects and locations. A project can have many locations by use of the locations.project_id reference. Furthermore, locations feature the columns locations.latitude and locations.longitude.
I'm now looking for an efficient way to query all projects and their minimal distance to a fixed point. Say, there are two projects in the table each with two locations, I'd like to get the two projects and the distance to the one location which is closest to the fixed point.
I've created a .geography
function for convenience:
CREATE FUNCTION geography(locations) RETURNS GEOGRAPHY AS $$
SELECT ST_GeographyFromText(
'SRID=4326;POINT(' || $1.latitude || ' ' || $1.longitude || ')'
)
$$ LANGUAGE SQL
Now the following example uses "Zurich (47.36865, 8.539183)" as the fixed point:
SELECT
ST_Distance(locations.geography, ST_GeographyFromText('SRID=4326;POINT(47.36865 8.539183)')) AS distance
FROM "projects"
INNER JOIN "locations" ON "locations"."project_id" = "projects"."id"
The query works, however, it returns the distance to the location with the oldest creation date and not the nearest one. Any idea how to tackle this?
UPDATE
My question is all wrong. What I try to achieve is quite easy with CTE, but that's another story. My apologies for the noise.
Upvotes: 0
Views: 204
Reputation: 43612
PostGIS uses the axis order: longitude, latitude (think X, Y). Your distances are probably not what you expect, as you are currently searching in Somalia, not Switzerland.
Furthermore, your approach is really inefficient, since you are constantly re-generation geography
types and have no spatial index. Consider moving the data into a spatial type, and removing redundant columns, e.g.:
ALTER TABLE locations ADD COLUMN geog geography(Point,4326);
UPDATE locations SET
geog=ST_SetSRID(ST_MakePoint(longitude, latitude), 4326)::geography;
CREATE INDEX locations_geog_idx ON locations USING gist (geog);
ALTER TABLE locations DROP COLUMN latitude;
ALTER TABLE locations DROP COLUMN longitude;
You can always access and view the data using ST_X
for longitude, or ST_AsText
or ST_AsLatLonText(geog::geometry)
for strings like 47°22'7.140"N 8°32'21.059"E. Now your query:
SELECT
ST_Distance(locations.geog,
ST_SetSRID(ST_MakePoint(8.539183, 47.36865), 4326)::geography)'))
AS distance
FROM "projects"
INNER JOIN "locations" ON "locations"."project_id" = "projects"."id";
Upvotes: 1