问题
A library I’m trying to use takes a given latitude and longitude and work out with entries in a table have a lat/lng within a certain distance from this. The generated SQL query works with MySQL, but not with PostgreSQL.
Here’s the entry in my server.log file detailing the error psql is giving and the full query:
ERROR: column "distance" does not exist at character 507
STATEMENT: select *, ( '3959' * acos( cos( radians('53.49') ) * cos( radians( places.lat ) ) * cos( radians( places.lng ) - radians('-2.38') ) + sin( radians('53.49') ) * sin( radians( places.lat ) ) ) ) AS distance from (
Select *
From places
Where places.lat Between 53.475527714192 And 53.504472285808
And places.lng Between -2.4043246788967 And -2.3556753211033
) As places where "places"."deleted_at" is null having "distance" <= $1 order by "distance" asc
Knowing what the SQL should be I can then edit the PHP code that generates it and send a PR back to the library.
回答1:
The query uses the syntax specific for MySql. In Postgres (and all other known to me RDBMS) you should use a derived table:
select *
from (
select *,
(3959 * acos( cos( radians(53.49) ) * cos( radians( places.lat ) )
* cos( radians( places.lng ) - radians(-2.38) )
+ sin( radians(53.49) ) * sin( radians( places.lat ) ) ) ) AS distance
from (
select *
from places
where places.lat between 53.475527714192 and 53.504472285808
and places.lng between -2.4043246788967 and -2.3556753211033
) as places
where places.deleted_at is null
) sub
where distance <= $1
order by distance asc
I have also removed quotes from numeric constants.
回答2:
What your query does is a replace the ST_Dwithin function of postgresql with a long query.
ST_DWithin — Returns true if the geometries are within the specified distance of one another. For geometry units are in those of spatial reference and For geography units are in meters and measurement is defaulted to use_spheroid=true (measure around spheroid), for faster check, use_spheroid=false to measure along sphere.
it's very simple just do
ST_DWithin(geometry1, geometry2)
where geometry1 and geometry2 can be point fields (but they can be other geometric data types two)
So your query might become
SELECT * FROM places WHERE
ST_DWithin(ST_GeomFromText('POINT(-71.060316 48.432044)', 4326), places.geom) ORDER BY ST_Distance (ST_GeomFromText('POINT(-71.060316 48.432044)', 4326), places.geom)
The only change that you will need to do is to put your lat, lng together into a single POINT Field. (your probably should do that even in mysql). You can continue to have your lat,lng in separate columns but you will be sacrificing the gains you can get by using a proper geo type.
Using ST_Dwithin will be faster than a manual distance query because it's designed to make use of geometry columns on postgresql which can be indexed.
What if you want to go back to mysql someday? Well mysql 5.7 added the St_Dwithin feature.
来源:https://stackoverflow.com/questions/33054139/how-can-i-make-this-geo-distance-sql-query-postgres-compatible