How can I make this geo-distance SQL query Postgres compatible

江枫思渺然 提交于 2019-12-22 23:21:24

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!